Merge "Fix issue with inconsistent stage visibility" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 371177c..45e33ce 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -95,6 +95,7 @@
"com.android.internal.foldables.flags-aconfig-java",
"com.android.internal.os.flags-aconfig-java",
"com.android.internal.pm.pkg.component.flags-aconfig-java",
+ "com.android.internal.widget.flags-aconfig-java",
"com.android.media.flags.bettertogether-aconfig-java",
"com.android.media.flags.editing-aconfig-java",
"com.android.media.flags.performance-aconfig-java",
@@ -279,6 +280,19 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+aconfig_declarations {
+ name: "com.android.internal.widget.flags-aconfig",
+ package: "com.android.internal.widget.flags",
+ container: "system",
+ srcs: ["core/java/com/android/internal/widget/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "com.android.internal.widget.flags-aconfig-java",
+ aconfig_declarations: "com.android.internal.widget.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Text
aconfig_declarations {
name: "com.android.text.flags-aconfig",
@@ -622,6 +636,11 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+cc_aconfig_library {
+ name: "aconfig_hardware_flags_c_lib",
+ aconfig_declarations: "android.hardware.flags-aconfig",
+}
+
// Widget
aconfig_declarations {
name: "android.widget.flags-aconfig",
@@ -789,21 +808,6 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
-// OnDeviceIntelligence
-aconfig_declarations {
- name: "android.app.ondeviceintelligence-aconfig",
- exportable: true,
- package: "android.app.ondeviceintelligence.flags",
- container: "system",
- srcs: ["core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig"],
-}
-
-java_aconfig_library {
- name: "android.app.ondeviceintelligence-aconfig-java",
- aconfig_declarations: "android.app.ondeviceintelligence-aconfig",
- defaults: ["framework-minus-apex-aconfig-java-defaults"],
-}
-
// Permissions
aconfig_declarations {
name: "android.permission.flags-aconfig",
@@ -985,6 +989,11 @@
java_aconfig_library {
name: "android.app.flags-aconfig-java",
aconfig_declarations: "android.app.flags-aconfig",
+ min_sdk_version: "34",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.nfcservices",
+ ],
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
@@ -1458,6 +1467,13 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "android.appwidget.flags-aconfig-java-host",
+ aconfig_declarations: "android.appwidget.flags-aconfig",
+ host_supported: true,
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// App
aconfig_declarations {
name: "android.server.app.flags-aconfig",
diff --git a/Android.bp b/Android.bp
index a525583b8..529da53 100644
--- a/Android.bp
+++ b/Android.bp
@@ -446,6 +446,9 @@
default: [
"framework-platformcrashrecovery.impl",
],
+ }) + select(release_flag("RELEASE_ONDEVICE_INTELLIGENCE_MODULE"), {
+ true: [],
+ default: ["framework-ondeviceintelligence-platform.impl"],
}),
sdk_version: "core_platform",
installable: false,
@@ -489,6 +492,7 @@
apex_available: ["//apex_available:platform"],
visibility: [
"//frameworks/base:__subpackages__",
+ "//packages/modules/NeuralNetworks:__subpackages__",
],
compile_dex: false,
headers_only: true,
@@ -584,6 +588,9 @@
default: [
"framework-platformcrashrecovery-compat-config",
],
+ }) + select(release_flag("RELEASE_ONDEVICE_INTELLIGENCE_MODULE"), {
+ true: [],
+ default: ["framework-ondeviceintelligence-platform-compat-config"],
}),
}
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
index 810be8f..fe95a59 100644
--- a/apex/jobscheduler/service/aconfig/job.aconfig
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -63,7 +63,7 @@
name: "remove_user_during_user_switch"
namespace: "backstage_power"
description: "Remove started user if user will be stopped due to user switch"
- bug: "321598070"
+ bug: "337077643"
}
flag {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index a5a08fb..8fad79a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1981,7 +1981,12 @@
jobStatus.getNumAppliedFlexibleConstraints(),
jobStatus.getNumDroppedFlexibleConstraints(),
jobStatus.getFilteredTraceTag(),
- jobStatus.getFilteredDebugTags());
+ jobStatus.getFilteredDebugTags(),
+ jobStatus.getNumAbandonedFailures(),
+ /* 0 is reserved for UNKNOWN_POLICY */
+ jobStatus.getJob().getBackoffPolicy() + 1,
+ shouldUseAggressiveBackoff(jobStatus.getNumAbandonedFailures()));
+
// If the job is immediately ready to run, then we can just immediately
// put it in the pending list and try to schedule it. This is especially
@@ -2422,7 +2427,11 @@
cancelled.getNumAppliedFlexibleConstraints(),
cancelled.getNumDroppedFlexibleConstraints(),
cancelled.getFilteredTraceTag(),
- cancelled.getFilteredDebugTags());
+ cancelled.getFilteredDebugTags(),
+ cancelled.getNumAbandonedFailures(),
+ /* 0 is reserved for UNKNOWN_POLICY */
+ cancelled.getJob().getBackoffPolicy() + 1,
+ shouldUseAggressiveBackoff(cancelled.getNumAbandonedFailures()));
}
// If this is a replacement, bring in the new version of the job
if (incomingJob != null) {
@@ -5917,9 +5926,6 @@
pw.print(Flags.FLAG_DO_NOT_FORCE_RUSH_EXECUTION_AT_BOOT,
Flags.doNotForceRushExecutionAtBoot());
pw.println();
- pw.print(android.app.job.Flags.FLAG_BACKUP_JOBS_EXEMPTION,
- android.app.job.Flags.backupJobsExemption());
- pw.println();
pw.print(android.app.job.Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND,
android.app.job.Flags.ignoreImportantWhileForeground());
pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
index f3bc9c7..42c8250 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -433,9 +433,6 @@
case com.android.server.job.Flags.FLAG_DO_NOT_FORCE_RUSH_EXECUTION_AT_BOOT:
pw.println(com.android.server.job.Flags.doNotForceRushExecutionAtBoot());
break;
- case android.app.job.Flags.FLAG_BACKUP_JOBS_EXEMPTION:
- pw.println(android.app.job.Flags.backupJobsExemption());
- break;
case android.app.job.Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND:
pw.println(android.app.job.Flags.ignoreImportantWhileForeground());
break;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 909a9b3..2b401c8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -546,7 +546,11 @@
job.getNumAppliedFlexibleConstraints(),
job.getNumDroppedFlexibleConstraints(),
job.getFilteredTraceTag(),
- job.getFilteredDebugTags());
+ job.getFilteredDebugTags(),
+ job.getNumAbandonedFailures(),
+ /* 0 is reserved for UNKNOWN_POLICY */
+ job.getJob().getBackoffPolicy() + 1,
+ mService.shouldUseAggressiveBackoff(job.getNumAbandonedFailures()));
sEnqueuedJwiAtJobStart.logSampleWithUid(job.getUid(), job.getWorkCount());
final String sourcePackage = job.getSourcePackageName();
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
@@ -1681,7 +1685,11 @@
completedJob.getNumAppliedFlexibleConstraints(),
completedJob.getNumDroppedFlexibleConstraints(),
completedJob.getFilteredTraceTag(),
- completedJob.getFilteredDebugTags());
+ completedJob.getFilteredDebugTags(),
+ completedJob.getNumAbandonedFailures(),
+ /* 0 is reserved for UNKNOWN_POLICY */
+ completedJob.getJob().getBackoffPolicy() + 1,
+ mService.shouldUseAggressiveBackoff(completedJob.getNumAbandonedFailures()));
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER,
JobSchedulerService.TRACE_TRACK_NAME, getId());
diff --git a/api/Android.bp b/api/Android.bp
index 7326203..14c2766 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -105,6 +105,13 @@
default: [
"framework-platformcrashrecovery",
],
+ }) + select(release_flag("RELEASE_ONDEVICE_INTELLIGENCE_MODULE"), {
+ true: [
+ "framework-ondeviceintelligence",
+ ],
+ default: [
+ "framework-ondeviceintelligence-platform",
+ ],
}) + select(release_flag("RELEASE_RANGING_STACK"), {
true: [
"framework-ranging",
@@ -119,7 +126,12 @@
"service-permission",
"service-rkp",
"service-sdksandbox",
- ] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
+ ] + select(release_flag("RELEASE_ONDEVICE_INTELLIGENCE_MODULE"), {
+ true: [
+ "service-ondeviceintelligence",
+ ],
+ default: [],
+ }) + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
"true": [
"service-crashrecovery",
],
@@ -478,6 +490,7 @@
"//frameworks/base/location",
"//frameworks/base/packages/CrashRecovery/framework",
"//frameworks/base/nfc",
+ "//packages/modules/NeuralNetworks:__subpackages__",
],
plugins: ["error_prone_android_framework"],
errorprone: {
diff --git a/api/api.go b/api/api.go
index 5ca24de..e4d783e 100644
--- a/api/api.go
+++ b/api/api.go
@@ -29,6 +29,7 @@
const virtualization = "framework-virtualization"
const location = "framework-location"
const platformCrashrecovery = "framework-platformcrashrecovery"
+const ondeviceintelligence = "framework-ondeviceintelligence-platform"
var core_libraries_modules = []string{art, conscrypt, i18n}
@@ -40,7 +41,7 @@
// APIs.
// In addition, the modules in this list are allowed to contribute to test APIs
// stubs.
-var non_updatable_modules = []string{virtualization, location, platformCrashrecovery}
+var non_updatable_modules = []string{virtualization, location, platformCrashrecovery, ondeviceintelligence}
// The intention behind this soong plugin is to generate a number of "merged"
// API-related modules that would otherwise require a large amount of very
diff --git a/boot/Android.bp b/boot/Android.bp
index 6eead42..eaa984a 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -31,6 +31,7 @@
"car_bootclasspath_fragment",
"nfc_apex_bootclasspath_fragment",
"release_crashrecovery_module",
+ "release_ondevice_intelligence_module",
"release_package_profiling_module",
],
properties: [
@@ -176,6 +177,15 @@
},
],
},
+ release_ondevice_intelligence_module: {
+ fragments: [
+ // only used when ondeviceintelligence is moved to neuralnetworks module
+ {
+ apex: "com.android.neuralnetworks",
+ module: "com.android.ondeviceintelligence-bootclasspath-fragment",
+ },
+ ],
+ },
release_package_profiling_module: {
fragments: [
// only used if profiling is enabled.
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 58763a7..d9ff190 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -165,6 +165,7 @@
],
host_supported: true,
test_suites: ["general-tests"],
+ require_root: true,
srcs: [
"tests/BinaryStreamVisitorTests.cpp",
"tests/CommandLineOptionsTests.cpp",
diff --git a/cmds/idmap2/libidmap2/ResourceContainer.cpp b/cmds/idmap2/libidmap2/ResourceContainer.cpp
index 57ae354..f22a481 100644
--- a/cmds/idmap2/libidmap2/ResourceContainer.cpp
+++ b/cmds/idmap2/libidmap2/ResourceContainer.cpp
@@ -22,6 +22,7 @@
#include <utility>
#include <vector>
+#include "android-base/scopeguard.h"
#include "androidfw/ApkAssets.h"
#include "androidfw/AssetManager.h"
#include "androidfw/Util.h"
@@ -269,27 +270,40 @@
std::unique_ptr<AssetManager2> am;
ZipAssetsProvider* zip_assets;
- static Result<ResState> Initialize(std::unique_ptr<ZipAssetsProvider> zip,
+ static Result<ResState> Initialize(std::unique_ptr<ZipAssetsProvider>&& zip,
package_property_t flags) {
ResState state;
state.zip_assets = zip.get();
if ((state.apk_assets = ApkAssets::Load(std::move(zip), flags)) == nullptr) {
- return Error("failed to load apk asset");
+ return Error("failed to load apk asset for '%s'",
+ state.zip_assets->GetDebugName().c_str());
}
+ // Make sure we put ZipAssetsProvider where we took it if initialization fails, so the
+ // original object stays valid for any next call it may get.
+ auto scoped_restore_zip_assets = android::base::ScopeGuard([&zip, &state]() {
+ zip = std::unique_ptr<ZipAssetsProvider>(
+ static_cast<ZipAssetsProvider*>(
+ std::move(const_cast<ApkAssets&>(*state.apk_assets)).TakeAssetsProvider().release()));
+ });
+
if ((state.arsc = state.apk_assets->GetLoadedArsc()) == nullptr) {
- return Error("failed to retrieve loaded arsc");
+ return Error("failed to retrieve loaded arsc for '%s'",
+ state.zip_assets->GetDebugName().c_str());
}
if ((state.package = GetPackageAtIndex0(state.arsc)) == nullptr) {
- return Error("failed to retrieve loaded package at index 0");
+ return Error("failed to retrieve loaded package at index 0 for '%s'",
+ state.zip_assets->GetDebugName().c_str());
}
state.am = std::make_unique<AssetManager2>();
if (!state.am->SetApkAssets({state.apk_assets}, false)) {
- return Error("failed to create asset manager");
+ return Error("failed to create asset manager for '%s'",
+ state.zip_assets->GetDebugName().c_str());
}
+ scoped_restore_zip_assets.Disable();
return state;
}
};
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 1b656e8..7093614 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -214,6 +214,20 @@
ASSERT_EQ(idmap->GetHeader()->GetOverlayName(), TestConstants::OVERLAY_NAME_ALL_POLICIES);
}
+TEST(IdmapTests, TargetContainerWorksAfterError) {
+ auto target = TargetResourceContainer::FromPath(GetTestDataPath() + "/target/target-bad.apk");
+ ASSERT_TRUE(target);
+
+ auto crc = target->get()->GetCrc();
+ ASSERT_TRUE(crc);
+
+ // This call tries to construct the full ApkAssets state, and fails.
+ ASSERT_FALSE(target->get()->DefinesOverlayable());
+ auto crc2 = target->get()->GetCrc();
+ ASSERT_TRUE(crc2);
+ EXPECT_EQ(*crc, *crc2);
+}
+
TEST(IdmapTests, CreateIdmapDataFromApkAssets) {
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk";
diff --git a/cmds/idmap2/tests/data/target/target-bad.apk b/cmds/idmap2/tests/data/target/target-bad.apk
new file mode 100644
index 0000000..fd86782
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/target-bad.apk
Binary files differ
diff --git a/core/api/current.txt b/core/api/current.txt
index f3d19ca..bbdcd6d 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -140,6 +140,7 @@
field public static final String MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL = "android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL";
field public static final String MANAGE_DEVICE_POLICY_AIRPLANE_MODE = "android.permission.MANAGE_DEVICE_POLICY_AIRPLANE_MODE";
field public static final String MANAGE_DEVICE_POLICY_APPS_CONTROL = "android.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL";
+ field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String MANAGE_DEVICE_POLICY_APP_FUNCTIONS = "android.permission.MANAGE_DEVICE_POLICY_APP_FUNCTIONS";
field public static final String MANAGE_DEVICE_POLICY_APP_RESTRICTIONS = "android.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS";
field public static final String MANAGE_DEVICE_POLICY_APP_USER_DATA = "android.permission.MANAGE_DEVICE_POLICY_APP_USER_DATA";
field public static final String MANAGE_DEVICE_POLICY_ASSIST_CONTENT = "android.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT";
@@ -2200,6 +2201,11 @@
field @FlaggedApi("android.os.material_motion_tokens") public static final int config_motionStandardFastSpatialDamping;
field @FlaggedApi("android.os.material_motion_tokens") public static final int config_motionStandardSlowEffectDamping;
field @FlaggedApi("android.os.material_motion_tokens") public static final int config_motionStandardSlowSpatialDamping;
+ field @FlaggedApi("android.os.material_shape_tokens") public static final int config_shapeCornerRadiusLarge;
+ field @FlaggedApi("android.os.material_shape_tokens") public static final int config_shapeCornerRadiusMedium;
+ field @FlaggedApi("android.os.material_shape_tokens") public static final int config_shapeCornerRadiusSmall;
+ field @FlaggedApi("android.os.material_shape_tokens") public static final int config_shapeCornerRadiusXlarge;
+ field @FlaggedApi("android.os.material_shape_tokens") public static final int config_shapeCornerRadiusXsmall;
field public static final int dialog_min_width_major = 17104899; // 0x1050003
field public static final int dialog_min_width_minor = 17104900; // 0x1050004
field public static final int notification_large_icon_height = 17104902; // 0x1050006
@@ -6476,6 +6482,7 @@
method public String getSortKey();
method public long getTimeoutAfter();
method public boolean hasImage();
+ method @FlaggedApi("android.app.api_rich_ongoing") public boolean hasPromotableCharacteristics();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT;
field public static final int BADGE_ICON_LARGE = 2; // 0x2
@@ -8074,6 +8081,7 @@
field public static final String ACCOUNT_MANAGEMENT_DISABLED_POLICY = "accountManagementDisabled";
field public static final String APPLICATION_HIDDEN_POLICY = "applicationHidden";
field public static final String APPLICATION_RESTRICTIONS_POLICY = "applicationRestrictions";
+ field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String APP_FUNCTIONS_POLICY = "appFunctions";
field public static final String AUTO_TIMEZONE_POLICY = "autoTimezone";
field public static final String AUTO_TIME_POLICY = "autoTime";
field public static final String BACKUP_SERVICE_POLICY = "backupService";
@@ -8123,6 +8131,7 @@
method @NonNull public java.util.Set<java.lang.String> getAffiliationIds(@NonNull android.content.ComponentName);
method @Nullable public java.util.Set<java.lang.String> getAlwaysOnVpnLockdownWhitelist(@NonNull android.content.ComponentName);
method @Nullable public String getAlwaysOnVpnPackage(@NonNull android.content.ComponentName);
+ method @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_FUNCTIONS, conditional=true) public int getAppFunctionsPolicy();
method @NonNull @WorkerThread public android.os.Bundle getApplicationRestrictions(@Nullable android.content.ComponentName, String);
method @Deprecated @Nullable public String getApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName);
method @RequiresPermission(anyOf={android.Manifest.permission.SET_TIME, "android.permission.QUERY_ADMIN_POLICY"}, conditional=true) public boolean getAutoTimeEnabled(@Nullable android.content.ComponentName);
@@ -8281,6 +8290,7 @@
method public void setAffiliationIds(@NonNull android.content.ComponentName, @NonNull java.util.Set<java.lang.String>);
method public void setAlwaysOnVpnPackage(@NonNull android.content.ComponentName, @Nullable String, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
method public void setAlwaysOnVpnPackage(@NonNull android.content.ComponentName, @Nullable String, boolean, @Nullable java.util.Set<java.lang.String>) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_FUNCTIONS, conditional=true) public void setAppFunctionsPolicy(int);
method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_PACKAGE_STATE, conditional=true) public boolean setApplicationHidden(@Nullable android.content.ComponentName, String, boolean);
method @WorkerThread public void setApplicationRestrictions(@Nullable android.content.ComponentName, String, android.os.Bundle);
method @Deprecated public void setApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName, @Nullable String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -8407,6 +8417,9 @@
field public static final String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD";
field public static final String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
field public static final String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
+ field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final int APP_FUNCTIONS_DISABLED = 1; // 0x1
+ field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final int APP_FUNCTIONS_DISABLED_CROSS_PROFILE = 2; // 0x2
+ field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final int APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY = 0; // 0x0
field @FlaggedApi("android.app.admin.flags.set_auto_time_enabled_coexistence") public static final int AUTO_TIME_DISABLED = 1; // 0x1
field @FlaggedApi("android.app.admin.flags.set_auto_time_enabled_coexistence") public static final int AUTO_TIME_ENABLED = 2; // 0x2
field @FlaggedApi("android.app.admin.flags.set_auto_time_enabled_coexistence") public static final int AUTO_TIME_NOT_CONTROLLED_BY_POLICY = 0; // 0x0
@@ -8871,6 +8884,7 @@
field public static final int ERROR_CATEGORY_UNKNOWN = 0; // 0x0
field public static final int ERROR_DENIED = 1000; // 0x3e8
field public static final int ERROR_DISABLED = 1002; // 0x3ea
+ field public static final int ERROR_ENTERPRISE_POLICY_DISALLOWED = 2002; // 0x7d2
field public static final int ERROR_FUNCTION_NOT_FOUND = 1003; // 0x3eb
field public static final int ERROR_INVALID_ARGUMENT = 1001; // 0x3e9
field public static final int ERROR_SYSTEM_ERROR = 2000; // 0x7d0
@@ -21781,6 +21795,18 @@
field public static final int ENCODING_DTS_UHD_P2 = 30; // 0x1e
field public static final int ENCODING_E_AC3 = 6; // 0x6
field public static final int ENCODING_E_AC3_JOC = 18; // 0x12
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC = 42; // 0x2a
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC = 43; // 0x2b
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS = 41; // 0x29
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM = 44; // 0x2c
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_BASE_PROFILE_AAC = 38; // 0x26
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_BASE_PROFILE_FLAC = 39; // 0x27
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_BASE_PROFILE_OPUS = 37; // 0x25
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_BASE_PROFILE_PCM = 40; // 0x28
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_SIMPLE_PROFILE_AAC = 34; // 0x22
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_SIMPLE_PROFILE_FLAC = 35; // 0x23
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_SIMPLE_PROFILE_OPUS = 33; // 0x21
+ field @FlaggedApi("android.media.audio.iamf_definitions_api") public static final int ENCODING_IAMF_SIMPLE_PROFILE_PCM = 36; // 0x24
field public static final int ENCODING_IEC61937 = 13; // 0xd
field public static final int ENCODING_INVALID = 0; // 0x0
field public static final int ENCODING_MP3 = 9; // 0x9
@@ -24902,7 +24928,7 @@
method @Nullable public android.net.Uri getIconUri();
method @NonNull public String getId();
method @NonNull public CharSequence getName();
- method @FlaggedApi("com.android.media.flags.enable_route_visibility_control_api") @NonNull public java.util.Set<java.lang.String> getRequiredPermissions();
+ method @FlaggedApi("com.android.media.flags.enable_route_visibility_control_api") @NonNull public java.util.List<java.util.Set<java.lang.String>> getRequiredPermissions();
method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public int getSuitabilityStatus();
method @FlaggedApi("com.android.media.flags.enable_mirroring_in_media_router_2") public int getSupportedRoutingTypes();
method public int getType();
@@ -24972,6 +24998,7 @@
method @NonNull public android.media.MediaRoute2Info.Builder setExtras(@Nullable android.os.Bundle);
method @NonNull public android.media.MediaRoute2Info.Builder setIconUri(@Nullable android.net.Uri);
method @FlaggedApi("com.android.media.flags.enable_route_visibility_control_api") @NonNull public android.media.MediaRoute2Info.Builder setRequiredPermissions(@NonNull java.util.Set<java.lang.String>);
+ method @FlaggedApi("com.android.media.flags.enable_route_visibility_control_api") @NonNull public android.media.MediaRoute2Info.Builder setRequiredPermissions(@NonNull java.util.List<java.util.Set<java.lang.String>>);
method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") @NonNull public android.media.MediaRoute2Info.Builder setSuitabilityStatus(int);
method @FlaggedApi("com.android.media.flags.enable_mirroring_in_media_router_2") @NonNull public android.media.MediaRoute2Info.Builder setSupportedRoutingTypes(int);
method @NonNull public android.media.MediaRoute2Info.Builder setType(int);
@@ -27143,6 +27170,15 @@
package android.media.quality {
+ @FlaggedApi("android.media.tv.flags.media_quality_fw") public final class ActiveProcessingPicture implements android.os.Parcelable {
+ ctor public ActiveProcessingPicture(int, @NonNull String);
+ method public int describeContents();
+ method public int getId();
+ method @NonNull public String getProfileId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.quality.ActiveProcessingPicture> CREATOR;
+ }
+
@FlaggedApi("android.media.tv.flags.media_quality_fw") public final class AmbientBacklightEvent implements android.os.Parcelable {
ctor public AmbientBacklightEvent(int, @Nullable android.media.quality.AmbientBacklightMetadata);
method public int describeContents();
@@ -27194,8 +27230,30 @@
}
public static final class MediaQualityContract.PictureQuality {
+ field public static final String PARAMETER_AUTO_PICTURE_QUALITY_ENABLED = "auto_picture_quality_enabled";
+ field public static final String PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED = "auto_super_resolution_enabled";
+ field public static final String PARAMETER_BLUE_STRETCH = "blue_stretch";
field public static final String PARAMETER_BRIGHTNESS = "brightness";
+ field public static final String PARAMETER_COLOR_TEMPERATURE = "color_temperature";
+ field public static final String PARAMETER_COLOR_TUNE = "color_tune";
+ field public static final String PARAMETER_COLOR_TUNER_BLUE_GAIN = "color_tuner_blue_gain";
+ field public static final String PARAMETER_COLOR_TUNER_BLUE_OFFSET = "color_tuner_blue_offset";
+ field public static final String PARAMETER_COLOR_TUNER_BRIGHTNESS = "color_tuner_brightness";
+ field public static final String PARAMETER_COLOR_TUNER_GREEN_GAIN = "color_tuner_green_gain";
+ field public static final String PARAMETER_COLOR_TUNER_GREEN_OFFSET = "color_tuner_green_offset";
+ field public static final String PARAMETER_COLOR_TUNER_HUE = "color_tuner_hue";
+ field public static final String PARAMETER_COLOR_TUNER_RED_GAIN = "color_tuner_red_gain";
+ field public static final String PARAMETER_COLOR_TUNER_RED_OFFSET = "color_tuner_red_offset";
+ field public static final String PARAMETER_COLOR_TUNER_SATURATION = "color_tuner_saturation";
field public static final String PARAMETER_CONTRAST = "contrast";
+ field public static final String PARAMETER_DECONTOUR = "decontour";
+ field public static final String PARAMETER_DYNAMIC_LUMA_CONTROL = "dynamic_luma_control";
+ field public static final String PARAMETER_FILM_MODE = "film_mode";
+ field public static final String PARAMETER_FLESH_TONE = "flesh_tone";
+ field public static final String PARAMETER_GLOBAL_DIMMING = "global_dimming";
+ field public static final String PARAMETER_HUE = "hue";
+ field public static final String PARAMETER_MPEG_NOISE_REDUCTION = "mpeg_noise_reduction";
+ field public static final String PARAMETER_NOISE_REDUCTION = "noise_reduction";
field public static final String PARAMETER_SATURATION = "saturation";
field public static final String PARAMETER_SHARPNESS = "sharpness";
}
@@ -27207,13 +27265,14 @@
}
@FlaggedApi("android.media.tv.flags.media_quality_fw") public final class MediaQualityManager {
+ method public void addActiveProcessingPictureListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.quality.MediaQualityManager.ActiveProcessingPictureListener);
method public void createPictureProfile(@NonNull android.media.quality.PictureProfile);
method public void createSoundProfile(@NonNull android.media.quality.SoundProfile);
- method @NonNull public java.util.List<android.media.quality.PictureProfile> getAvailablePictureProfiles();
- method @NonNull public java.util.List<android.media.quality.SoundProfile> getAvailableSoundProfiles();
+ method @NonNull public java.util.List<android.media.quality.PictureProfile> getAvailablePictureProfiles(boolean);
+ method @NonNull public java.util.List<android.media.quality.SoundProfile> getAvailableSoundProfiles(boolean);
method @NonNull public java.util.List<android.media.quality.ParamCapability> getParamCapabilities(@NonNull java.util.List<java.lang.String>);
- method @Nullable public android.media.quality.PictureProfile getPictureProfile(int, @NonNull String);
- method @Nullable public android.media.quality.SoundProfile getSoundProfile(int, @NonNull String);
+ method @Nullable public android.media.quality.PictureProfile getPictureProfile(int, @NonNull String, boolean);
+ method @Nullable public android.media.quality.SoundProfile getSoundProfile(int, @NonNull String, boolean);
method public boolean isAmbientBacklightEnabled();
method public boolean isAutoPictureQualityEnabled();
method public boolean isAutoSoundQualityEnabled();
@@ -27221,6 +27280,7 @@
method public void registerAmbientBacklightCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.quality.MediaQualityManager.AmbientBacklightCallback);
method public void registerPictureProfileCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.quality.MediaQualityManager.PictureProfileCallback);
method public void registerSoundProfileCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.quality.MediaQualityManager.SoundProfileCallback);
+ method public void removeActiveProcessingPictureListener(@NonNull android.media.quality.MediaQualityManager.ActiveProcessingPictureListener);
method public void removePictureProfile(@NonNull String);
method public void removeSoundProfile(@NonNull String);
method public void setAmbientBacklightEnabled(boolean);
@@ -27232,6 +27292,10 @@
method public void updateSoundProfile(@NonNull String, @NonNull android.media.quality.SoundProfile);
}
+ public static interface MediaQualityManager.ActiveProcessingPictureListener {
+ method public void onActiveProcessingPicturesChanged(@NonNull java.util.List<android.media.quality.ActiveProcessingPicture>);
+ }
+
public abstract static class MediaQualityManager.AmbientBacklightCallback {
ctor public MediaQualityManager.AmbientBacklightCallback();
method public void onAmbientBacklightEvent(@NonNull android.media.quality.AmbientBacklightEvent);
@@ -27239,7 +27303,7 @@
public abstract static class MediaQualityManager.PictureProfileCallback {
ctor public MediaQualityManager.PictureProfileCallback();
- method public void onError(int);
+ method public void onError(@Nullable String, int);
method public void onParamCapabilitiesChanged(@Nullable String, @NonNull java.util.List<android.media.quality.ParamCapability>);
method public void onPictureProfileAdded(@NonNull String, @NonNull android.media.quality.PictureProfile);
method public void onPictureProfileRemoved(@NonNull String, @NonNull android.media.quality.PictureProfile);
@@ -27248,7 +27312,7 @@
public abstract static class MediaQualityManager.SoundProfileCallback {
ctor public MediaQualityManager.SoundProfileCallback();
- method public void onError(int);
+ method public void onError(@Nullable String, int);
method public void onParamCapabilitiesChanged(@Nullable String, @NonNull java.util.List<android.media.quality.ParamCapability>);
method public void onSoundProfileAdded(@NonNull String, @NonNull android.media.quality.SoundProfile);
method public void onSoundProfileRemoved(@NonNull String, @NonNull android.media.quality.SoundProfile);
@@ -34701,7 +34765,10 @@
method public android.os.MessageQueue getMessageQueue();
method public boolean hasMessages(android.os.Handler, Object, int);
method public boolean hasMessages(android.os.Handler, Object, Runnable);
+ method @FlaggedApi("android.os.message_queue_testability") public boolean isBlockedOnSyncBarrier();
method public android.os.Message next();
+ method @FlaggedApi("android.os.message_queue_testability") @Nullable public Long peekWhen();
+ method @FlaggedApi("android.os.message_queue_testability") @Nullable public android.os.Message pop();
method public void recycle(android.os.Message);
method public void release();
}
@@ -40754,8 +40821,10 @@
method @NonNull public java.util.List<java.security.cert.X509Certificate> getGrantedCertificateChainFromId(long) throws android.security.keystore.KeyPermanentlyInvalidatedException, java.security.UnrecoverableKeyException;
method @NonNull public java.security.Key getGrantedKeyFromId(long) throws android.security.keystore.KeyPermanentlyInvalidatedException, java.security.UnrecoverableKeyException;
method @NonNull public java.security.KeyPair getGrantedKeyPairFromId(long) throws android.security.keystore.KeyPermanentlyInvalidatedException, java.security.UnrecoverableKeyException;
+ method @FlaggedApi("android.security.keystore2.attest_modules") @NonNull public byte[] getSupplementaryAttestationInfo(int) throws android.security.KeyStoreException;
method public long grantKeyAccess(@NonNull String, int) throws android.security.KeyStoreException, java.security.UnrecoverableKeyException;
method public void revokeKeyAccess(@NonNull String, int) throws android.security.KeyStoreException, java.security.UnrecoverableKeyException;
+ field public static final int MODULE_HASH = -1879047468; // 0x900002d4
}
public class SecureKeyImportUnavailableException extends java.security.ProviderException {
@@ -40979,7 +41048,7 @@
field public static final int TYPE_DATASET_SELECTED = 0; // 0x0
field public static final int TYPE_SAVE_SHOWN = 3; // 0x3
field public static final int TYPE_VIEW_REQUESTED_AUTOFILL = 6; // 0x6
- field @FlaggedApi("android.service.autofill.autofill_w_metrics") public static final int UI_TYPE_CREDMAN = 4; // 0x4
+ field @FlaggedApi("android.service.autofill.autofill_w_metrics") public static final int UI_TYPE_CREDENTIAL_MANAGER = 4; // 0x4
field public static final int UI_TYPE_DIALOG = 3; // 0x3
field public static final int UI_TYPE_INLINE = 2; // 0x2
field public static final int UI_TYPE_MENU = 1; // 0x1
@@ -42548,9 +42617,10 @@
method public boolean isWritable();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.SettingsPreferenceMetadata> CREATOR;
- field public static final int INTENT_ONLY = 2; // 0x2
- field public static final int NOT_SENSITIVE = 0; // 0x0
- field public static final int SENSITIVE = 1; // 0x1
+ field public static final int EXPECT_POST_CONFIRMATION = 1; // 0x1
+ field public static final int EXPECT_PRE_CONFIRMATION = 2; // 0x2
+ field public static final int NO_DIRECT_ACCESS = 3; // 0x3
+ field public static final int NO_SENSITIVITY = 0; // 0x0
}
public static final class SettingsPreferenceMetadata.Builder {
@@ -44818,11 +44888,11 @@
field public static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY = "carrier_vvm_package_name_string_array";
field public static final String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
field public static final String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL = "carrier_wfc_supports_wifi_only_bool";
- field public static final String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int";
- field public static final String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
- field public static final String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
- field public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
- field public static final String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
field public static final String KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY = "cellular_service_capabilities_int_array";
field public static final String KEY_CELLULAR_USAGE_SETTING_INT = "cellular_usage_setting_int";
field public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL = "check_pricing_with_carrier_data_roaming_bool";
@@ -44849,7 +44919,7 @@
field public static final String KEY_DEFAULT_VM_NUMBER_ROAMING_AND_IMS_UNREGISTERED_STRING = "default_vm_number_roaming_and_ims_unregistered_string";
field public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
field public static final String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
- field public static final String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
field public static final String KEY_DISABLE_CHARGE_INDICATION_BOOL = "disable_charge_indication_bool";
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL = "disable_dun_apn_while_roaming_with_preset_apn_bool";
field public static final String KEY_DISABLE_SUPPLEMENTARY_SERVICES_IN_AIRPLANE_MODE_BOOL = "disable_supplementary_services_in_airplane_mode_bool";
@@ -44970,6 +45040,7 @@
field public static final String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string";
field public static final String KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY = "read_only_apn_fields_string_array";
field public static final String KEY_READ_ONLY_APN_TYPES_STRING_ARRAY = "read_only_apn_types_string_array";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_REGIONAL_SATELLITE_EARFCN_BUNDLE = "regional_satellite_earfcn_bundle";
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL = "remove_satellite_plmn_in_manual_network_scan_bool";
field public static final String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
field @Deprecated public static final String KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL = "restart_radio_on_pdp_fail_regular_deactivation_bool";
@@ -44983,6 +45054,7 @@
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ATTACH_SUPPORTED_BOOL = "satellite_attach_supported_bool";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT = "satellite_connection_hysteresis_sec_int";
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_DATA_SUPPORT_MODE_INT = "satellite_data_support_mode_int";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_DISPLAY_NAME_STRING = "satellite_display_name_string";
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_ENTITLEMENT_APP_NAME_STRING = "satellite_entitlement_app_name_string";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT = "satellite_entitlement_status_refresh_days_int";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL = "satellite_entitlement_supported_bool";
@@ -44994,12 +45066,14 @@
field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL = "satellite_roaming_p2p_sms_supported_bool";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT = "satellite_roaming_screen_off_inactivity_timeout_sec_int";
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL = "satellite_roaming_turn_off_session_for_emergency_call_bool";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE_BYTES_INT = "satellite_sos_max_datagram_size_bytes_int";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_SUPPORTED_MSG_APPS_STRING_ARRAY = "satellite_supported_msg_apps_string_array";
field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
- field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
field public static final String KEY_SHOW_BLOCKING_PAY_PHONE_OPTION_BOOL = "show_blocking_pay_phone_option_bool";
field public static final String KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL = "show_call_blocking_disabled_notification_always_bool";
- field public static final String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
field public static final String KEY_SHOW_FORWARDED_NUMBER_BOOL = "show_forwarded_number_bool";
field public static final String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
field public static final String KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL = "show_ims_registration_status_bool";
@@ -45029,7 +45103,7 @@
field public static final String KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL = "support_enhanced_call_blocking_bool";
field public static final String KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL = "support_ims_conference_event_package_bool";
field public static final String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
- field public static final String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
field public static final String KEY_SUPPORT_TDSCDMA_BOOL = "support_tdscdma_bool";
field public static final String KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY = "support_tdscdma_roaming_networks_string_array";
field public static final String KEY_SWITCH_DATA_TO_PRIMARY_IF_PRIMARY_IS_OOS_BOOL = "switch_data_to_primary_if_primary_is_oos_bool";
@@ -45039,7 +45113,7 @@
field public static final String KEY_USE_ACS_FOR_RCS_BOOL = "use_acs_for_rcs_bool";
field public static final String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool";
field public static final String KEY_USE_IP_FOR_CALLING_INDICATOR_BOOL = "use_ip_for_calling_indicator_bool";
- field public static final String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
field @Deprecated public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
field public static final String KEY_USE_RCS_SIP_OPTIONS_BOOL = "use_rcs_sip_options_bool";
field public static final String KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL = "use_wfc_home_network_mode_in_roaming_network_bool";
@@ -45475,13 +45549,13 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellIdentity> CREATOR;
}
- public final class CellIdentityCdma extends android.telephony.CellIdentity {
- method public int getBasestationId();
- method public int getLatitude();
- method public int getLongitude();
- method public int getNetworkId();
- method public int getSystemId();
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellIdentityCdma> CREATOR;
+ @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public final class CellIdentityCdma extends android.telephony.CellIdentity {
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public int getBasestationId();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public int getLatitude();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public int getLongitude();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public int getNetworkId();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public int getSystemId();
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellIdentityCdma> CREATOR;
}
public final class CellIdentityGsm extends android.telephony.CellIdentity {
@@ -45573,11 +45647,11 @@
field public static final long UNAVAILABLE_LONG = 9223372036854775807L; // 0x7fffffffffffffffL
}
- public final class CellInfoCdma extends android.telephony.CellInfo implements android.os.Parcelable {
- method @NonNull public android.telephony.CellIdentityCdma getCellIdentity();
- method @NonNull public android.telephony.CellSignalStrengthCdma getCellSignalStrength();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellInfoCdma> CREATOR;
+ @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public final class CellInfoCdma extends android.telephony.CellInfo implements android.os.Parcelable {
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @NonNull public android.telephony.CellIdentityCdma getCellIdentity();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @NonNull public android.telephony.CellSignalStrengthCdma getCellSignalStrength();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public void writeToParcel(android.os.Parcel, int);
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellInfoCdma> CREATOR;
}
public final class CellInfoGsm extends android.telephony.CellInfo implements android.os.Parcelable {
@@ -47156,11 +47230,11 @@
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getImei(int);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}) public String getLine1Number();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public String getManualNetworkSelectionPlmn();
- method @Nullable public String getManufacturerCode();
- method @Nullable public String getManufacturerCode(int);
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @Nullable public String getManufacturerCode();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @Nullable public String getManufacturerCode(int);
method public static long getMaximumCallComposerPictureSize();
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getMeid();
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getMeid(int);
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getMeid();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getMeid(int);
method public String getMmsUAProfUrl();
method public String getMmsUserAgent();
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getNai();
@@ -47306,10 +47380,10 @@
field public static final int CARRIER_RESTRICTION_STATUS_RESTRICTED = 2; // 0x2
field public static final int CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER = 3; // 0x3
field public static final int CARRIER_RESTRICTION_STATUS_UNKNOWN = 0; // 0x0
- field public static final int CDMA_ROAMING_MODE_AFFILIATED = 1; // 0x1
- field public static final int CDMA_ROAMING_MODE_ANY = 2; // 0x2
- field public static final int CDMA_ROAMING_MODE_HOME = 0; // 0x0
- field public static final int CDMA_ROAMING_MODE_RADIO_DEFAULT = -1; // 0xffffffff
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_ROAMING_MODE_AFFILIATED = 1; // 0x1
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_ROAMING_MODE_ANY = 2; // 0x2
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_ROAMING_MODE_HOME = 0; // 0x0
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_ROAMING_MODE_RADIO_DEFAULT = -1; // 0xffffffff
field public static final int DATA_ACTIVITY_DORMANT = 4; // 0x4
field public static final int DATA_ACTIVITY_IN = 1; // 0x1
field public static final int DATA_ACTIVITY_INOUT = 3; // 0x3
@@ -47329,9 +47403,9 @@
field public static final int DATA_SUSPENDED = 3; // 0x3
field public static final int DATA_UNKNOWN = -1; // 0xffffffff
field public static final int DEFAULT_PORT_INDEX = 0; // 0x0
- field public static final int ERI_FLASH = 2; // 0x2
- field public static final int ERI_OFF = 1; // 0x1
- field public static final int ERI_ON = 0; // 0x0
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int ERI_FLASH = 2; // 0x2
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int ERI_OFF = 1; // 0x1
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int ERI_ON = 0; // 0x0
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final String EVENT_DISPLAY_EMERGENCY_MESSAGE = "android.telephony.event.DISPLAY_EMERGENCY_MESSAGE";
field public static final String EXTRA_ACTIVE_SIM_SUPPORTED_COUNT = "android.telephony.extra.ACTIVE_SIM_SUPPORTED_COUNT";
field public static final String EXTRA_APN_PROTOCOL = "android.telephony.extra.APN_PROTOCOL";
@@ -47372,11 +47446,11 @@
field public static final int NETWORK_SELECTION_MODE_AUTO = 1; // 0x1
field public static final int NETWORK_SELECTION_MODE_MANUAL = 2; // 0x2
field public static final int NETWORK_SELECTION_MODE_UNKNOWN = 0; // 0x0
- field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L
- field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L
- field public static final long NETWORK_TYPE_BITMASK_EHRPD = 8192L; // 0x2000L
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final long NETWORK_TYPE_BITMASK_EHRPD = 8192L; // 0x2000L
field public static final long NETWORK_TYPE_BITMASK_EVDO_0 = 16L; // 0x10L
field public static final long NETWORK_TYPE_BITMASK_EVDO_A = 32L; // 0x20L
field public static final long NETWORK_TYPE_BITMASK_EVDO_B = 2048L; // 0x800L
@@ -47389,16 +47463,17 @@
field public static final long NETWORK_TYPE_BITMASK_IWLAN = 131072L; // 0x20000L
field public static final long NETWORK_TYPE_BITMASK_LTE = 4096L; // 0x1000L
field @Deprecated public static final long NETWORK_TYPE_BITMASK_LTE_CA = 262144L; // 0x40000L
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final long NETWORK_TYPE_BITMASK_NB_IOT_NTN = 1048576L; // 0x100000L
field public static final long NETWORK_TYPE_BITMASK_NR = 524288L; // 0x80000L
field public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = 65536L; // 0x10000L
field public static final long NETWORK_TYPE_BITMASK_UMTS = 4L; // 0x4L
field public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L; // 0x0L
- field public static final int NETWORK_TYPE_CDMA = 4; // 0x4
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int NETWORK_TYPE_CDMA = 4; // 0x4
field public static final int NETWORK_TYPE_EDGE = 2; // 0x2
- field public static final int NETWORK_TYPE_EHRPD = 14; // 0xe
- field public static final int NETWORK_TYPE_EVDO_0 = 5; // 0x5
- field public static final int NETWORK_TYPE_EVDO_A = 6; // 0x6
- field public static final int NETWORK_TYPE_EVDO_B = 12; // 0xc
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int NETWORK_TYPE_EHRPD = 14; // 0xe
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int NETWORK_TYPE_EVDO_0 = 5; // 0x5
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int NETWORK_TYPE_EVDO_A = 6; // 0x6
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int NETWORK_TYPE_EVDO_B = 12; // 0xc
field public static final int NETWORK_TYPE_GPRS = 1; // 0x1
field public static final int NETWORK_TYPE_GSM = 16; // 0x10
field public static final int NETWORK_TYPE_HSDPA = 8; // 0x8
@@ -47408,11 +47483,12 @@
field @Deprecated public static final int NETWORK_TYPE_IDEN = 11; // 0xb
field public static final int NETWORK_TYPE_IWLAN = 18; // 0x12
field public static final int NETWORK_TYPE_LTE = 13; // 0xd
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int NETWORK_TYPE_NB_IOT_NTN = 21; // 0x15
field public static final int NETWORK_TYPE_NR = 20; // 0x14
field public static final int NETWORK_TYPE_TD_SCDMA = 17; // 0x11
field public static final int NETWORK_TYPE_UMTS = 3; // 0x3
field public static final int NETWORK_TYPE_UNKNOWN = 0; // 0x0
- field public static final int PHONE_TYPE_CDMA = 2; // 0x2
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int PHONE_TYPE_CDMA = 2; // 0x2
field public static final int PHONE_TYPE_GSM = 1; // 0x1
field public static final int PHONE_TYPE_NONE = 0; // 0x0
field public static final int PHONE_TYPE_SIP = 3; // 0x3
@@ -55176,8 +55252,8 @@
method public abstract void setTransformation(android.graphics.Matrix);
method public abstract void setVisibility(int);
method public abstract void setWebDomain(@Nullable String);
- field @FlaggedApi("android.service.autofill.autofill_w_metrics") public static final String EXTRA_VIRTUAL_STRUCTURE_TYPE = "android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_TYPE";
- field @FlaggedApi("android.service.autofill.autofill_w_metrics") public static final String EXTRA_VIRTUAL_STRUCTURE_VERSION_NUMBER = "android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER";
+ field @FlaggedApi("android.service.autofill.autofill_w_metrics") public static final String EXTRA_VIRTUAL_STRUCTURE_TYPE = "android.view.extra.VIRTUAL_STRUCTURE_TYPE";
+ field @FlaggedApi("android.service.autofill.autofill_w_metrics") public static final String EXTRA_VIRTUAL_STRUCTURE_VERSION_NUMBER = "android.view.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER";
}
public abstract static class ViewStructure.HtmlInfo {
@@ -58290,6 +58366,7 @@
method @NonNull @WorkerThread public default android.view.textclassifier.TextSelection suggestSelection(@NonNull android.view.textclassifier.TextSelection.Request);
method @NonNull @WorkerThread public default android.view.textclassifier.TextSelection suggestSelection(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @Nullable android.os.LocaleList);
field public static final String EXTRA_FROM_TEXT_CLASSIFIER = "android.view.textclassifier.extra.FROM_TEXT_CLASSIFIER";
+ field @FlaggedApi("android.permission.flags.text_classifier_choice_api_enabled") public static final String EXTRA_TEXT_ORIGIN_PACKAGE = "android.view.textclassifier.extra.TEXT_ORIGIN_PACKAGE";
field public static final String HINT_TEXT_IS_EDITABLE = "android.text_is_editable";
field public static final String HINT_TEXT_IS_NOT_EDITABLE = "android.text_is_not_editable";
field public static final android.view.textclassifier.TextClassifier NO_OP;
@@ -58299,6 +58376,7 @@
field public static final String TYPE_EMAIL = "email";
field public static final String TYPE_FLIGHT_NUMBER = "flight";
field public static final String TYPE_OTHER = "other";
+ field @FlaggedApi("android.permission.flags.text_classifier_choice_api_enabled") public static final String TYPE_OTP = "otp";
field public static final String TYPE_PHONE = "phone";
field public static final String TYPE_UNKNOWN = "";
field public static final String TYPE_URL = "url";
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index ad5bd31..e71dffa 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -1,10 +1,4 @@
// Baseline format: 1.0
-ActionValue: android.view.ViewStructure#EXTRA_VIRTUAL_STRUCTURE_TYPE:
- Inconsistent extra value; expected `android.view.extra.VIRTUAL_STRUCTURE_TYPE`, was `android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_TYPE`
-ActionValue: android.view.ViewStructure#EXTRA_VIRTUAL_STRUCTURE_VERSION_NUMBER:
- Inconsistent extra value; expected `android.view.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER`, was `android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER`
-
-
BroadcastBehavior: android.app.AlarmManager#ACTION_NEXT_ALARM_CLOCK_CHANGED:
Field 'ACTION_NEXT_ALARM_CLOCK_CHANGED' is missing @BroadcastBehavior
BroadcastBehavior: android.app.AlarmManager#ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED:
@@ -1191,10 +1185,6 @@
New API must be flagged with @FlaggedApi: field android.R.dimen.system_corner_radius_xlarge
UnflaggedApi: android.R.dimen#system_corner_radius_xsmall:
New API must be flagged with @FlaggedApi: field android.R.dimen.system_corner_radius_xsmall
-UnflaggedApi: android.R.integer#status_bar_notification_info_maxnum:
- Changes from not deprecated to deprecated must be flagged with @FlaggedApi: field android.R.integer.status_bar_notification_info_maxnum
-UnflaggedApi: android.R.string#status_bar_notification_info_overflow:
- Changes from not deprecated to deprecated must be flagged with @FlaggedApi: field android.R.string.status_bar_notification_info_overflow
UnflaggedApi: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_INTERNAL_ERROR:
New API must be flagged with @FlaggedApi: field android.accessibilityservice.AccessibilityService.OVERLAY_RESULT_INTERNAL_ERROR
UnflaggedApi: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_INVALID:
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index fe6e995..bca2ce2 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -482,6 +482,10 @@
field public static final long TRACE_TAG_NETWORK = 2097152L; // 0x200000L
}
+ public class UpdateEngine {
+ method @FlaggedApi("android.os.update_engine_api") public void triggerPostinstall(@NonNull String);
+ }
+
}
package android.os.storage {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d1de19e..9590e1a 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10,6 +10,7 @@
field @FlaggedApi("android.app.contextualsearch.flags.enable_service") public static final String ACCESS_CONTEXTUAL_SEARCH = "android.permission.ACCESS_CONTEXTUAL_SEARCH";
field public static final String ACCESS_CONTEXT_HUB = "android.permission.ACCESS_CONTEXT_HUB";
field public static final String ACCESS_DRM_CERTIFICATES = "android.permission.ACCESS_DRM_CERTIFICATES";
+ field @FlaggedApi("android.permission.flags.fine_power_monitor_permission") public static final String ACCESS_FINE_POWER_MONITORS = "android.permission.ACCESS_FINE_POWER_MONITORS";
field @Deprecated public static final String ACCESS_FM_RADIO = "android.permission.ACCESS_FM_RADIO";
field public static final String ACCESS_FPS_COUNTER = "android.permission.ACCESS_FPS_COUNTER";
field @FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles") public static final String ACCESS_HIDDEN_PROFILES_FULL = "android.permission.ACCESS_HIDDEN_PROFILES_FULL";
@@ -26,6 +27,7 @@
field public static final String ACCESS_SHORTCUTS = "android.permission.ACCESS_SHORTCUTS";
field @FlaggedApi("android.app.smartspace.flags.access_smartspace") public static final String ACCESS_SMARTSPACE = "android.permission.ACCESS_SMARTSPACE";
field public static final String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER";
+ field @FlaggedApi("android.permission.flags.text_classifier_choice_api_enabled") public static final String ACCESS_TEXT_CLASSIFIER_BY_TYPE = "android.permission.ACCESS_TEXT_CLASSIFIER_BY_TYPE";
field public static final String ACCESS_TUNED_INFO = "android.permission.ACCESS_TUNED_INFO";
field public static final String ACCESS_TV_DESCRAMBLER = "android.permission.ACCESS_TV_DESCRAMBLER";
field public static final String ACCESS_TV_SHARED_FILTER = "android.permission.ACCESS_TV_SHARED_FILTER";
@@ -328,6 +330,7 @@
field public static final String READ_RUNTIME_PROFILES = "android.permission.READ_RUNTIME_PROFILES";
field public static final String READ_SAFETY_CENTER_STATUS = "android.permission.READ_SAFETY_CENTER_STATUS";
field public static final String READ_SEARCH_INDEXABLES = "android.permission.READ_SEARCH_INDEXABLES";
+ field @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") public static final String READ_SUBSCRIPTION_PLANS = "android.permission.READ_SUBSCRIPTION_PLANS";
field @FlaggedApi("android.app.system_terms_of_address_enabled") public static final String READ_SYSTEM_GRAMMATICAL_GENDER = "android.permission.READ_SYSTEM_GRAMMATICAL_GENDER";
field public static final String READ_SYSTEM_UPDATE_INFO = "android.permission.READ_SYSTEM_UPDATE_INFO";
field public static final String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL";
@@ -550,6 +553,7 @@
field public static final int config_systemTextIntelligence = 17039414; // 0x1040036
field public static final int config_systemUi = 17039418; // 0x104003a
field public static final int config_systemUiIntelligence = 17039410; // 0x1040032
+ field @FlaggedApi("android.permission.flags.system_vendor_intelligence_role_enabled") public static final int config_systemVendorIntelligence;
field public static final int config_systemVisualIntelligence = 17039415; // 0x1040037
field public static final int config_systemWearHealthService = 17039428; // 0x1040044
field public static final int config_systemWellbeing = 17039408; // 0x1040030
@@ -2275,149 +2279,6 @@
}
-package android.app.ondeviceintelligence {
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public interface DownloadCallback {
- method public void onDownloadCompleted(@NonNull android.os.PersistableBundle);
- method public void onDownloadFailed(int, @Nullable String, @NonNull android.os.PersistableBundle);
- method public default void onDownloadProgress(long);
- method public default void onDownloadStarted(long);
- field public static final int DOWNLOAD_FAILURE_STATUS_DOWNLOADING = 3; // 0x3
- field public static final int DOWNLOAD_FAILURE_STATUS_NETWORK_FAILURE = 2; // 0x2
- field public static final int DOWNLOAD_FAILURE_STATUS_NOT_ENOUGH_DISK_SPACE = 1; // 0x1
- field public static final int DOWNLOAD_FAILURE_STATUS_UNAVAILABLE = 4; // 0x4
- field public static final int DOWNLOAD_FAILURE_STATUS_UNKNOWN = 0; // 0x0
- }
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class Feature implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public android.os.PersistableBundle getFeatureParams();
- method public int getId();
- method @Nullable public String getModelName();
- method @Nullable public String getName();
- method public int getType();
- method public int getVariant();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.ondeviceintelligence.Feature> CREATOR;
- }
-
- public static final class Feature.Builder {
- ctor public Feature.Builder(int);
- method @NonNull public android.app.ondeviceintelligence.Feature build();
- method @NonNull public android.app.ondeviceintelligence.Feature.Builder setFeatureParams(@NonNull android.os.PersistableBundle);
- method @NonNull public android.app.ondeviceintelligence.Feature.Builder setModelName(@NonNull String);
- method @NonNull public android.app.ondeviceintelligence.Feature.Builder setName(@NonNull String);
- method @NonNull public android.app.ondeviceintelligence.Feature.Builder setType(int);
- method @NonNull public android.app.ondeviceintelligence.Feature.Builder setVariant(int);
- }
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class FeatureDetails implements android.os.Parcelable {
- ctor public FeatureDetails(int, @NonNull android.os.PersistableBundle);
- ctor public FeatureDetails(int);
- method public int describeContents();
- method @NonNull public android.os.PersistableBundle getFeatureDetailParams();
- method public int getFeatureStatus();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.ondeviceintelligence.FeatureDetails> CREATOR;
- field public static final int FEATURE_STATUS_AVAILABLE = 3; // 0x3
- field public static final int FEATURE_STATUS_DOWNLOADABLE = 1; // 0x1
- field public static final int FEATURE_STATUS_DOWNLOADING = 2; // 0x2
- field public static final int FEATURE_STATUS_SERVICE_UNAVAILABLE = 4; // 0x4
- field public static final int FEATURE_STATUS_UNAVAILABLE = 0; // 0x0
- }
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence_module") public final class InferenceInfo implements android.os.Parcelable {
- method public int describeContents();
- method public long getEndTimeMillis();
- method public long getStartTimeMillis();
- method public long getSuspendedTimeMillis();
- method public int getUid();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.ondeviceintelligence.InferenceInfo> CREATOR;
- }
-
- public static final class InferenceInfo.Builder {
- ctor public InferenceInfo.Builder(int);
- method @NonNull public android.app.ondeviceintelligence.InferenceInfo build();
- method @NonNull public android.app.ondeviceintelligence.InferenceInfo.Builder setEndTimeMillis(long);
- method @NonNull public android.app.ondeviceintelligence.InferenceInfo.Builder setStartTimeMillis(long);
- method @NonNull public android.app.ondeviceintelligence.InferenceInfo.Builder setSuspendedTimeMillis(long);
- }
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public class OnDeviceIntelligenceException extends java.lang.Exception {
- ctor public OnDeviceIntelligenceException(int, @NonNull String, @NonNull android.os.PersistableBundle);
- ctor public OnDeviceIntelligenceException(int, @NonNull android.os.PersistableBundle);
- ctor public OnDeviceIntelligenceException(int, @NonNull String);
- ctor public OnDeviceIntelligenceException(int);
- method public int getErrorCode();
- method @NonNull public android.os.PersistableBundle getErrorParams();
- field public static final int ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE = 100; // 0x64
- field public static final int PROCESSING_ERROR_BAD_DATA = 2; // 0x2
- field public static final int PROCESSING_ERROR_BAD_REQUEST = 3; // 0x3
- field public static final int PROCESSING_ERROR_BUSY = 9; // 0x9
- field public static final int PROCESSING_ERROR_CANCELLED = 7; // 0x7
- field public static final int PROCESSING_ERROR_COMPUTE_ERROR = 5; // 0x5
- field public static final int PROCESSING_ERROR_INTERNAL = 14; // 0xe
- field public static final int PROCESSING_ERROR_IPC_ERROR = 6; // 0x6
- field public static final int PROCESSING_ERROR_NOT_AVAILABLE = 8; // 0x8
- field public static final int PROCESSING_ERROR_REQUEST_NOT_SAFE = 4; // 0x4
- field public static final int PROCESSING_ERROR_REQUEST_TOO_LARGE = 12; // 0xc
- field public static final int PROCESSING_ERROR_RESPONSE_NOT_SAFE = 11; // 0xb
- field public static final int PROCESSING_ERROR_SAFETY_ERROR = 10; // 0xa
- field public static final int PROCESSING_ERROR_SERVICE_UNAVAILABLE = 15; // 0xf
- field public static final int PROCESSING_ERROR_SUSPENDED = 13; // 0xd
- field public static final int PROCESSING_ERROR_UNKNOWN = 1; // 0x1
- field public static final int PROCESSING_UPDATE_STATUS_CONNECTION_FAILED = 200; // 0xc8
- }
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class OnDeviceIntelligenceManager {
- method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void getFeature(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.Feature,android.app.ondeviceintelligence.OnDeviceIntelligenceException>);
- method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void getFeatureDetails(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.FeatureDetails,android.app.ondeviceintelligence.OnDeviceIntelligenceException>);
- method @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence_module") @NonNull @RequiresPermission(android.Manifest.permission.DUMP) public java.util.List<android.app.ondeviceintelligence.InferenceInfo> getLatestInferenceInfo(long);
- method @Nullable @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public String getRemoteServicePackageName();
- method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void getVersion(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.LongConsumer);
- method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void listFeatures(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.util.List<android.app.ondeviceintelligence.Feature>,android.app.ondeviceintelligence.OnDeviceIntelligenceException>);
- method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void processRequest(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.os.Bundle, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull java.util.concurrent.Executor, @NonNull android.app.ondeviceintelligence.ProcessingCallback);
- method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void processRequestStreaming(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.os.Bundle, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull java.util.concurrent.Executor, @NonNull android.app.ondeviceintelligence.StreamingProcessingCallback);
- method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void requestFeatureDownload(@NonNull android.app.ondeviceintelligence.Feature, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.app.ondeviceintelligence.DownloadCallback);
- method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void requestTokenInfo(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.os.Bundle, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.TokenInfo,android.app.ondeviceintelligence.OnDeviceIntelligenceException>);
- field public static final int REQUEST_TYPE_EMBEDDINGS = 2; // 0x2
- field public static final int REQUEST_TYPE_INFERENCE = 0; // 0x0
- field public static final int REQUEST_TYPE_PREPARE = 1; // 0x1
- }
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public interface ProcessingCallback {
- method public default void onDataAugmentRequest(@NonNull android.os.Bundle, @NonNull java.util.function.Consumer<android.os.Bundle>);
- method public void onError(@NonNull android.app.ondeviceintelligence.OnDeviceIntelligenceException);
- method public void onResult(@NonNull android.os.Bundle);
- }
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class ProcessingSignal {
- ctor public ProcessingSignal();
- method public void sendSignal(@NonNull android.os.PersistableBundle);
- method public void setOnProcessingSignalCallback(@NonNull java.util.concurrent.Executor, @Nullable android.app.ondeviceintelligence.ProcessingSignal.OnProcessingSignalCallback);
- }
-
- public static interface ProcessingSignal.OnProcessingSignalCallback {
- method public void onSignalReceived(@NonNull android.os.PersistableBundle);
- }
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public interface StreamingProcessingCallback extends android.app.ondeviceintelligence.ProcessingCallback {
- method public void onPartialResult(@NonNull android.os.Bundle);
- }
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class TokenInfo implements android.os.Parcelable {
- ctor public TokenInfo(long, @NonNull android.os.PersistableBundle);
- ctor public TokenInfo(long);
- method public int describeContents();
- method public long getCount();
- method @NonNull public android.os.PersistableBundle getInfoParams();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.ondeviceintelligence.TokenInfo> CREATOR;
- }
-
-}
-
package android.app.people {
public final class PeopleManager {
@@ -5264,9 +5125,9 @@
}
@FlaggedApi("android.chre.flags.offload_api") public class HubEndpointSession implements java.lang.AutoCloseable {
- method public void close();
- method @Nullable public android.hardware.contexthub.HubServiceInfo getServiceInfo();
- method @NonNull public android.hardware.location.ContextHubTransaction<java.lang.Void> sendMessage(@NonNull android.hardware.contexthub.HubMessage);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void close();
+ method @Nullable public String getServiceDescriptor();
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> sendMessage(@NonNull android.hardware.contexthub.HubMessage);
}
@FlaggedApi("android.chre.flags.offload_api") public class HubEndpointSessionResult {
@@ -5318,7 +5179,7 @@
@FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointLifecycleCallback {
method public void onSessionClosed(@NonNull android.hardware.contexthub.HubEndpointSession, int);
- method @NonNull public android.hardware.contexthub.HubEndpointSessionResult onSessionOpenRequest(@NonNull android.hardware.contexthub.HubEndpointInfo, @Nullable android.hardware.contexthub.HubServiceInfo);
+ method @NonNull public android.hardware.contexthub.HubEndpointSessionResult onSessionOpenRequest(@NonNull android.hardware.contexthub.HubEndpointInfo, @Nullable String);
method public void onSessionOpened(@NonNull android.hardware.contexthub.HubEndpointSession);
}
@@ -6324,7 +6185,7 @@
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int loadNanoApp(int, @NonNull android.hardware.location.NanoApp);
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> loadNanoApp(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.NanoAppBinary);
method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void openSession(@NonNull android.hardware.contexthub.HubEndpoint, @NonNull android.hardware.contexthub.HubEndpointInfo);
- method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void openSession(@NonNull android.hardware.contexthub.HubEndpoint, @NonNull android.hardware.contexthub.HubEndpointInfo, @NonNull android.hardware.contexthub.HubServiceInfo);
+ method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void openSession(@NonNull android.hardware.contexthub.HubEndpoint, @NonNull android.hardware.contexthub.HubEndpointInfo, @NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.util.List<android.hardware.location.NanoAppState>> queryNanoApps(@NonNull android.hardware.location.ContextHubInfo);
method @Deprecated public int registerCallback(@NonNull android.hardware.location.ContextHubManager.Callback);
method @Deprecated public int registerCallback(android.hardware.location.ContextHubManager.Callback, android.os.Handler);
@@ -8128,12 +7989,13 @@
package android.media.quality {
@FlaggedApi("android.media.tv.flags.media_quality_fw") public final class MediaQualityManager {
+ method public void addGlobalActiveProcessingPictureListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.quality.MediaQualityManager.ActiveProcessingPictureListener);
method @NonNull public java.util.List<java.lang.String> getPictureProfileAllowList();
method @NonNull public java.util.List<java.lang.String> getPictureProfilePackageNames();
- method @NonNull public java.util.List<android.media.quality.PictureProfile> getPictureProfilesByPackage(@NonNull String);
+ method @NonNull public java.util.List<android.media.quality.PictureProfile> getPictureProfilesByPackage(@NonNull String, boolean);
method @NonNull public java.util.List<java.lang.String> getSoundProfileAllowList();
method @NonNull public java.util.List<java.lang.String> getSoundProfilePackageNames();
- method @NonNull public java.util.List<android.media.quality.SoundProfile> getSoundProfilesByPackage(@NonNull String);
+ method @NonNull public java.util.List<android.media.quality.SoundProfile> getSoundProfilesByPackage(@NonNull String, boolean);
method public void setAutoPictureQualityEnabled(boolean);
method public void setAutoSoundQualityEnabled(boolean);
method public boolean setDefaultPictureProfile(@Nullable String);
@@ -12615,6 +12477,7 @@
field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
field public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS";
field public static final String ACTION_SHOW_RESTRICTED_SETTING_DIALOG = "android.settings.SHOW_RESTRICTED_SETTING_DIALOG";
+ field @FlaggedApi("com.android.internal.telephony.flags.action_sim_preference_settings") public static final String ACTION_SIM_PREFERENCE_SETTINGS = "android.settings.SIM_PREFERENCE_SETTINGS";
field public static final String ACTION_TETHER_PROVISIONING_UI = "android.settings.TETHER_PROVISIONING_UI";
field public static final String ACTION_TETHER_SETTINGS = "android.settings.TETHER_SETTINGS";
field public static final String ACTION_TETHER_UNSUPPORTED_CARRIER_UI = "android.settings.TETHER_UNSUPPORTED_CARRIER_UI";
@@ -12850,14 +12713,14 @@
}
@FlaggedApi("android.security.secure_lockdown") public final class DisableSecureLockDeviceParams implements android.os.Parcelable {
- ctor public DisableSecureLockDeviceParams(@NonNull String);
+ ctor public DisableSecureLockDeviceParams(@NonNull CharSequence);
method public int describeContents();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.security.authenticationpolicy.DisableSecureLockDeviceParams> CREATOR;
}
@FlaggedApi("android.security.secure_lockdown") public final class EnableSecureLockDeviceParams implements android.os.Parcelable {
- ctor public EnableSecureLockDeviceParams(@NonNull String);
+ ctor public EnableSecureLockDeviceParams(@NonNull CharSequence);
method public int describeContents();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.security.authenticationpolicy.EnableSecureLockDeviceParams> CREATOR;
@@ -13789,39 +13652,6 @@
}
-package android.service.ondeviceintelligence {
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public abstract class OnDeviceIntelligenceService extends android.app.Service {
- ctor public OnDeviceIntelligenceService();
- method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
- method public abstract void onDownloadFeature(int, @NonNull android.app.ondeviceintelligence.Feature, @Nullable android.os.CancellationSignal, @NonNull android.app.ondeviceintelligence.DownloadCallback);
- method public abstract void onGetFeature(int, int, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.Feature,android.app.ondeviceintelligence.OnDeviceIntelligenceException>);
- method public abstract void onGetFeatureDetails(int, @NonNull android.app.ondeviceintelligence.Feature, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.FeatureDetails,android.app.ondeviceintelligence.OnDeviceIntelligenceException>);
- method public abstract void onGetReadOnlyFeatureFileDescriptorMap(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,android.os.ParcelFileDescriptor>>);
- method public abstract void onGetVersion(@NonNull java.util.function.LongConsumer);
- method public abstract void onInferenceServiceConnected();
- method public abstract void onInferenceServiceDisconnected();
- method public abstract void onListFeatures(int, @NonNull android.os.OutcomeReceiver<java.util.List<android.app.ondeviceintelligence.Feature>,android.app.ondeviceintelligence.OnDeviceIntelligenceException>);
- method public final void updateProcessingState(@NonNull android.os.Bundle, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.PersistableBundle,android.app.ondeviceintelligence.OnDeviceIntelligenceException>);
- field public static final String SERVICE_INTERFACE = "android.service.ondeviceintelligence.OnDeviceIntelligenceService";
- }
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public abstract class OnDeviceSandboxedInferenceService extends android.app.Service {
- ctor public OnDeviceSandboxedInferenceService();
- method public final void fetchFeatureFileDescriptorMap(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,android.os.ParcelFileDescriptor>>);
- method @NonNull public java.util.concurrent.Executor getCallbackExecutor();
- method public final void getReadOnlyFileDescriptor(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.os.ParcelFileDescriptor>) throws java.io.FileNotFoundException;
- method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
- method @NonNull public abstract void onProcessRequest(int, @NonNull android.app.ondeviceintelligence.Feature, @NonNull android.os.Bundle, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull android.app.ondeviceintelligence.ProcessingCallback);
- method @NonNull public abstract void onProcessRequestStreaming(int, @NonNull android.app.ondeviceintelligence.Feature, @NonNull android.os.Bundle, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull android.app.ondeviceintelligence.StreamingProcessingCallback);
- method @NonNull public abstract void onTokenInfoRequest(int, @NonNull android.app.ondeviceintelligence.Feature, @NonNull android.os.Bundle, @Nullable android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.TokenInfo,android.app.ondeviceintelligence.OnDeviceIntelligenceException>);
- method public abstract void onUpdateProcessingState(@NonNull android.os.Bundle, @NonNull android.os.OutcomeReceiver<android.os.PersistableBundle,android.app.ondeviceintelligence.OnDeviceIntelligenceException>);
- method public final java.io.FileInputStream openFileInput(@NonNull String) throws java.io.FileNotFoundException;
- field public static final String SERVICE_INTERFACE = "android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService";
- }
-
-}
-
package android.service.persistentdata {
@FlaggedApi("android.security.frp_enforcement") public class PersistentDataBlockManager {
@@ -14802,7 +14632,7 @@
field public static final int CALLTYPE_INCOMING = 1; // 0x1
field public static final int CALLTYPE_OUTGOING = 2; // 0x2
field public static final int CALLTYPE_UNKNOWN = 0; // 0x0
- field public static final int CDMA_PHONE = 1; // 0x1
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_PHONE = 1; // 0x1
field @NonNull public static final android.os.Parcelable.Creator<android.telecom.ParcelableCallAnalytics> CREATOR;
field public static final int GSM_PHONE = 2; // 0x2
field public static final int IMS_PHONE = 4; // 0x4
@@ -15183,7 +15013,7 @@
field public static final String KEY_GBA_UA_SECURITY_ORGANIZATION_INT = "gba_ua_security_organization_int";
field public static final String KEY_GBA_UA_SECURITY_PROTOCOL_INT = "gba_ua_security_protocol_int";
field public static final String KEY_GBA_UA_TLS_CIPHER_SUITE_INT = "gba_ua_tls_cipher_suite_int";
- field public static final String KEY_SUPPORT_CDMA_1X_VOICE_CALLS_BOOL = "support_cdma_1x_voice_calls_bool";
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final String KEY_SUPPORT_CDMA_1X_VOICE_CALLS_BOOL = "support_cdma_1x_voice_calls_bool";
}
public static final class CarrierConfigManager.Wifi {
@@ -15270,8 +15100,8 @@
ctor public CellBroadcastService();
method @NonNull @WorkerThread public abstract CharSequence getCellBroadcastAreaInfo(int);
method @CallSuper public android.os.IBinder onBind(@Nullable android.content.Intent);
- method public abstract void onCdmaCellBroadcastSms(int, @NonNull byte[], int);
- method public abstract void onCdmaScpMessage(int, @NonNull java.util.List<android.telephony.cdma.CdmaSmsCbProgramData>, @NonNull String, @NonNull java.util.function.Consumer<android.os.Bundle>);
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public void onCdmaCellBroadcastSms(int, @NonNull byte[], int);
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public void onCdmaScpMessage(int, @NonNull java.util.List<android.telephony.cdma.CdmaSmsCbProgramData>, @NonNull String, @NonNull java.util.function.Consumer<android.os.Bundle>);
method public abstract void onGsmCellBroadcastSms(int, @NonNull byte[]);
field public static final String CELL_BROADCAST_SERVICE_INTERFACE = "android.telephony.CellBroadcastService";
}
@@ -15281,9 +15111,9 @@
method @NonNull public abstract android.telephony.CellIdentity sanitizeLocationInfo();
}
- public final class CellIdentityCdma extends android.telephony.CellIdentity {
- method @NonNull public android.telephony.cdma.CdmaCellLocation asCellLocation();
- method @NonNull public android.telephony.CellIdentityCdma sanitizeLocationInfo();
+ @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public final class CellIdentityCdma extends android.telephony.CellIdentity {
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @NonNull public android.telephony.cdma.CdmaCellLocation asCellLocation();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @NonNull public android.telephony.CellIdentityCdma sanitizeLocationInfo();
}
public final class CellIdentityGsm extends android.telephony.CellIdentity {
@@ -15699,16 +15529,16 @@
field public static final int BUSY = 17; // 0x11
field public static final int CALL_BARRED = 240; // 0xf0
field public static final int CALL_REJECTED = 21; // 0x15
- field public static final int CDMA_ACCESS_BLOCKED = 1009; // 0x3f1
- field public static final int CDMA_ACCESS_FAILURE = 1006; // 0x3ee
- field public static final int CDMA_DROP = 1001; // 0x3e9
- field public static final int CDMA_INTERCEPT = 1002; // 0x3ea
- field public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000; // 0x3e8
- field public static final int CDMA_NOT_EMERGENCY = 1008; // 0x3f0
- field public static final int CDMA_PREEMPTED = 1007; // 0x3ef
- field public static final int CDMA_REORDER = 1003; // 0x3eb
- field public static final int CDMA_RETRY_ORDER = 1005; // 0x3ed
- field public static final int CDMA_SO_REJECT = 1004; // 0x3ec
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_ACCESS_BLOCKED = 1009; // 0x3f1
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_ACCESS_FAILURE = 1006; // 0x3ee
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_DROP = 1001; // 0x3e9
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_INTERCEPT = 1002; // 0x3ea
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000; // 0x3e8
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_NOT_EMERGENCY = 1008; // 0x3f0
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_PREEMPTED = 1007; // 0x3ef
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_REORDER = 1003; // 0x3eb
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_RETRY_ORDER = 1005; // 0x3ed
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_SO_REJECT = 1004; // 0x3ec
field public static final int CHANNEL_NOT_AVAIL = 44; // 0x2c
field public static final int CHANNEL_UNACCEPTABLE = 6; // 0x6
field public static final int CONDITIONAL_IE_ERROR = 100; // 0x64
@@ -16242,14 +16072,14 @@
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getCarrierServicePackageName();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getCarrierServicePackageNameForLogicalSlot(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaEnhancedRoamingIndicatorDisplayNumber();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin(int);
- method public String getCdmaPrlVersion();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaRoamingMode();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaSubscriptionMode();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaEnhancedRoamingIndicatorDisplayNumber();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int);
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin(int);
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public String getCdmaPrlVersion();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaRoamingMode();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaSubscriptionMode();
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS) public java.util.List<android.telephony.CellBroadcastIdRange> getCellBroadcastIdRanges();
method public int getCurrentPhoneType();
method public int getCurrentPhoneType(int);
@@ -16339,7 +16169,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetCarrierKeysForImsiEncryption();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void resetIms(int);
method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void resetOtaEmergencyNumberDbFilePath();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig();
method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void resetSettings();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int sendThermalMitigationRequest(@NonNull android.telephony.ThermalMitigationRequest);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
@@ -16348,8 +16178,8 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallWaitingEnabled(boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCdmaRoamingMode(int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCdmaSubscriptionMode(int);
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCdmaRoamingMode(int);
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCdmaSubscriptionMode(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS) public void setCellBroadcastIdRanges(@NonNull java.util.List<android.telephony.CellBroadcastIdRange>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
@@ -16413,9 +16243,9 @@
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
- field public static final int CDMA_SUBSCRIPTION_NV = 1; // 0x1
- field public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; // 0x0
- field public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1; // 0xffffffff
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_SUBSCRIPTION_NV = 1; // 0x1
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; // 0x0
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1; // 0xffffffff
field public static final int CELL_BROADCAST_RESULT_FAIL_ACTIVATION = 3; // 0x3
field public static final int CELL_BROADCAST_RESULT_FAIL_CONFIG = 2; // 0x2
field public static final int CELL_BROADCAST_RESULT_SUCCESS = 0; // 0x0
@@ -16632,21 +16462,21 @@
package android.telephony.cdma {
- public final class CdmaSmsCbProgramData implements android.os.Parcelable {
- method public int describeContents();
- method public int getCategory();
- method public int getOperation();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final int CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY = 4099; // 0x1003
- field public static final int CATEGORY_CMAS_EXTREME_THREAT = 4097; // 0x1001
- field public static final int CATEGORY_CMAS_LAST_RESERVED_VALUE = 4351; // 0x10ff
- field public static final int CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT = 4096; // 0x1000
- field public static final int CATEGORY_CMAS_SEVERE_THREAT = 4098; // 0x1002
- field public static final int CATEGORY_CMAS_TEST_MESSAGE = 4100; // 0x1004
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.cdma.CdmaSmsCbProgramData> CREATOR;
- field public static final int OPERATION_ADD_CATEGORY = 1; // 0x1
- field public static final int OPERATION_CLEAR_CATEGORIES = 2; // 0x2
- field public static final int OPERATION_DELETE_CATEGORY = 0; // 0x0
+ @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public final class CdmaSmsCbProgramData implements android.os.Parcelable {
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public int describeContents();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public int getCategory();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public int getOperation();
+ method @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public void writeToParcel(android.os.Parcel, int);
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY = 4099; // 0x1003
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CATEGORY_CMAS_EXTREME_THREAT = 4097; // 0x1001
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CATEGORY_CMAS_LAST_RESERVED_VALUE = 4351; // 0x10ff
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT = 4096; // 0x1000
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CATEGORY_CMAS_SEVERE_THREAT = 4098; // 0x1002
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CATEGORY_CMAS_TEST_MESSAGE = 4100; // 0x1004
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") @NonNull public static final android.os.Parcelable.Creator<android.telephony.cdma.CdmaSmsCbProgramData> CREATOR;
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int OPERATION_ADD_CATEGORY = 1; // 0x1
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int OPERATION_CLEAR_CATEGORIES = 2; // 0x2
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int OPERATION_DELETE_CATEGORY = 0; // 0x0
}
}
@@ -17974,7 +17804,7 @@
field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; // 0x1
field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7; // 0x7
field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6; // 0x6
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 4; // 0x4
+ field @Deprecated @FlaggedApi("com.android.internal.telephony.flags.deprecate_cdma") public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 4; // 0x4
field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 5; // 0x5
field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_INTERNET_PDN = 12; // 0xc
field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 9; // 0x9
@@ -18823,6 +18653,10 @@
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteDatagramReceived(long, @NonNull android.telephony.satellite.SatelliteDatagram, int, @NonNull java.util.function.Consumer<java.lang.Void>);
}
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public interface SatelliteDisallowedReasonsCallback {
+ method public void onSatelliteDisallowedReasonsChanged(@NonNull int[]);
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SatelliteInfo implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<java.lang.Integer> getBands();
@@ -18838,6 +18672,7 @@
method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionSatellite(@NonNull java.util.List<android.telephony.satellite.SatelliteSubscriberInfo>, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionService(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.Set<java.lang.Integer> getAttachRestrictionReasonsForCarrier(int);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int[] getSatelliteDisallowedReasons();
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.List<java.lang.String> getSatellitePlmnsForCarrier(int);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatellite(@NonNull java.util.List<android.telephony.satellite.SatelliteSubscriberInfo>, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
@@ -18848,6 +18683,8 @@
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteModemStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void registerForNtnSignalStrengthChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.NtnSignalStrengthCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void registerForSatelliteDisallowedReasonsChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDisallowedReasonsCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSelectedNbIotSatelliteSubscriptionChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SelectedNbIotSatelliteSubscriptionCallback);
method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSupportedStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteSupportedStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void removeAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestAttachEnabledForCarrier(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
@@ -18861,7 +18698,10 @@
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsProvisioned(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void requestIsSupported(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestNtnSignalStrength(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.NtnSignalStrength,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteAccessConfigurationForCurrentLocation(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.SatelliteAccessConfiguration,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteDisplayName(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.CharSequence,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteSubscriberProvisionStatus(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.util.List<android.telephony.satellite.SatelliteSubscriberProvisionStatus>,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSelectedNbIotSatelliteSubscriptionId(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Integer,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestTimeForNextSatelliteVisibility(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.time.Duration,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void sendDatagram(int, @NonNull android.telephony.satellite.SatelliteDatagram, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void setDeviceAlignedWithSatellite(boolean);
@@ -18873,6 +18713,8 @@
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForModemStateChanged(@NonNull android.telephony.satellite.SatelliteModemStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrengthCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForProvisionStateChanged(@NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteDisallowedReasonsChanged(@NonNull android.telephony.satellite.SatelliteDisallowedReasonsCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSelectedNbIotSatelliteSubscriptionChanged(@NonNull android.telephony.satellite.SelectedNbIotSatelliteSubscriptionCallback);
method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSupportedStateChanged(@NonNull android.telephony.satellite.SatelliteSupportedStateCallback);
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String ACTION_SATELLITE_START_NON_EMERGENCY_SESSION = "android.telephony.satellite.action.SATELLITE_START_NON_EMERGENCY_SESSION";
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED = "android.telephony.satellite.action.SATELLITE_SUBSCRIBER_ID_LIST_CHANGED";
@@ -18944,6 +18786,7 @@
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_NOT_REACHABLE = 18; // 0x12
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_NOT_SUPPORTED = 20; // 0x14
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_NO_RESOURCES = 12; // 0xc
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_RESULT_NO_VALID_SATELLITE_SUBSCRIPTION = 30; // 0x1e
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_RADIO_NOT_AVAILABLE = 10; // 0xa
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_REQUEST_ABORTED = 15; // 0xf
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_REQUEST_FAILED = 9; // 0x9
@@ -19046,6 +18889,10 @@
method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public default void onSendDatagramStateChanged(int, int, int, int);
}
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public interface SelectedNbIotSatelliteSubscriptionCallback {
+ method public void onSelectedNbIotSatelliteSubscriptionChanged(int);
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SystemSelectionSpecifier implements android.os.Parcelable {
method public int describeContents();
method @NonNull public int[] getBands();
@@ -19291,6 +19138,20 @@
}
+package android.view.textclassifier {
+
+ public final class TextClassificationManager {
+ method @FlaggedApi("android.permission.flags.text_classifier_choice_api_enabled") @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TEXT_CLASSIFIER_BY_TYPE) public android.view.textclassifier.TextClassifier getClassifier(int);
+ }
+
+ public interface TextClassifier {
+ field @FlaggedApi("android.permission.flags.text_classifier_choice_api_enabled") public static final int CLASSIFIER_TYPE_ANDROID_DEFAULT = 2; // 0x2
+ field @FlaggedApi("android.permission.flags.text_classifier_choice_api_enabled") public static final int CLASSIFIER_TYPE_DEVICE_DEFAULT = 1; // 0x1
+ field @FlaggedApi("android.permission.flags.text_classifier_choice_api_enabled") public static final int CLASSIFIER_TYPE_SELF_PROVIDED = 0; // 0x0
+ }
+
+}
+
package android.view.translation {
public final class TranslationCapability implements android.os.Parcelable {
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index 7c43891..3b9ef959 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -505,6 +505,8 @@
FlaggedApiLiteral: android.Manifest.permission#ACCESS_LAST_KNOWN_CELL_ID:
@FlaggedApi contains a string literal, but should reference the field generated by aconfig (com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES).
+FlaggedApiLiteral: android.Manifest.permission#ACCESS_TEXT_CLASSIFIER_BY_TYPE:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.permission.flags.Flags.FLAG_TEXT_CLASSIFIER_CHOICE_API_ENABLED).
FlaggedApiLiteral: android.Manifest.permission#BACKUP_HEALTH_CONNECT_DATA_AND_SETTINGS:
@FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.permission.flags.Flags.FLAG_HEALTH_CONNECT_BACKUP_RESTORE_PERMISSION_ENABLED).
FlaggedApiLiteral: android.Manifest.permission#BIND_VERIFICATION_AGENT:
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 5442ab7..6230a59 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -657,6 +657,7 @@
field public static final int OPERATION_SET_ALWAYS_ON_VPN_PACKAGE = 30; // 0x1e
field public static final int OPERATION_SET_APPLICATION_HIDDEN = 15; // 0xf
field public static final int OPERATION_SET_APPLICATION_RESTRICTIONS = 16; // 0x10
+ field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final int OPERATION_SET_APP_FUNCTIONS_POLICY = 42; // 0x2a
field public static final int OPERATION_SET_CAMERA_DISABLED = 31; // 0x1f
field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final int OPERATION_SET_CONTENT_PROTECTION_POLICY = 41; // 0x29
field public static final int OPERATION_SET_FACTORY_RESET_PROTECTION_POLICY = 32; // 0x20
@@ -3246,14 +3247,6 @@
}
-package android.service.ondeviceintelligence {
-
- @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public abstract class OnDeviceIntelligenceService extends android.app.Service {
- method public void onReady();
- }
-
-}
-
package android.service.quickaccesswallet {
public interface QuickAccessWalletClient extends java.io.Closeable {
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 9140bdf..349b4ed 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -1,10 +1,4 @@
// Baseline format: 1.0
-ActionValue: android.view.contentcapture.ViewNode.ViewStructureImpl#EXTRA_VIRTUAL_STRUCTURE_TYPE:
- Inconsistent extra value; expected `android.view.contentcapture.extra.VIRTUAL_STRUCTURE_TYPE`, was `android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_TYPE`
-ActionValue: android.view.contentcapture.ViewNode.ViewStructureImpl#EXTRA_VIRTUAL_STRUCTURE_VERSION_NUMBER:
- Inconsistent extra value; expected `android.view.contentcapture.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER`, was `android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER`
-
-
BannedThrow: android.os.vibrator.persistence.VibrationXmlSerializer#serialize(android.os.VibrationEffect, java.io.Writer):
Methods must not throw unchecked exceptions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 38aea64..03ef669 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1279,7 +1279,24 @@
*
* <p>This method should be utilized when an activity wants to nudge the user to switch
* to the web application in cases where the web may provide the user with a better
- * experience. Note that this method does not guarantee that the education will be shown.</p>
+ * experience. Note that this method does not guarantee that the education will be shown.
+ *
+ * <p>The number of times that the "Open in browser" education can be triggered by this method
+ * is limited per application, and, when shown, the education appears above the app's content.
+ * For these reasons, developers should use this method sparingly when it is least
+ * disruptive to the user to show the education and when it is optimal to switch the user to a
+ * browser session. Before requesting to show the education, developers should assert that they
+ * have set a link that can be used by the "Open in browser" feature through either
+ * {@link AssistContent#EXTRA_AUTHENTICATING_USER_WEB_URI} or
+ * {@link AssistContent#setWebUri} so that users are navigated to a relevant page if they choose
+ * to switch to the browser. If a URI is not set using either method, "Open in browser" will
+ * utilize a generic link if available which will direct users to the homepage of the site
+ * associated with the app. The generic link is provided for a limited number of applications by
+ * the system and cannot be edited by developers. If none of these options contains a valid URI,
+ * the user will not be provided with the option to switch to the browser and the education will
+ * not be shown if requested.
+ *
+ * @see android.app.assist.AssistContent#EXTRA_SESSION_TRANSFER_WEB_URI
*/
@FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB_EDUCATION)
public final void requestOpenInBrowserEducation() {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 33ba058..69d3e8d 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1189,6 +1189,18 @@
return procState == PROCESS_STATE_FOREGROUND_SERVICE;
}
+ /** @hide Should this process state be considered jank perceptible? */
+ public static final boolean isProcStateJankPerceptible(int procState) {
+ if (Flags.jankPerceptibleNarrow()) {
+ return procState == PROCESS_STATE_PERSISTENT_UI
+ || procState == PROCESS_STATE_TOP
+ || procState == PROCESS_STATE_IMPORTANT_FOREGROUND
+ || procState == PROCESS_STATE_TOP_SLEEPING;
+ } else {
+ return !isProcStateCached(procState);
+ }
+ }
+
/** @hide requestType for assist context: only basic information. */
public static final int ASSIST_CONTEXT_BASIC = 0;
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index eccb6ff..abdfb535 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -44,6 +44,8 @@
import android.os.PowerExemptionManager.TempAllowListType;
import android.os.TransactionTooLargeException;
import android.os.WorkSource;
+import android.os.instrumentation.IOffsetCallback;
+import android.os.instrumentation.MethodDescriptor;
import android.util.ArraySet;
import android.util.Pair;
@@ -1352,6 +1354,14 @@
String reason, int exitInfoReason);
/**
+ * Queries the offset data for a given method on a process.
+ * @hide
+ */
+ public abstract void getExecutableMethodFileOffsets(@NonNull String processName,
+ int pid, int uid, @NonNull MethodDescriptor methodDescriptor,
+ @NonNull IOffsetCallback callback);
+
+ /**
* Add a creator token for all embedded intents (stored as extra) of the given intent.
*
* @param intent The given intent
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5d0b355..48b74f2 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -165,6 +165,10 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.instrumentation.ExecutableMethodFileOffsets;
+import android.os.instrumentation.IOffsetCallback;
+import android.os.instrumentation.MethodDescriptor;
+import android.os.instrumentation.MethodDescriptorParser;
import android.permission.IPermissionManager;
import android.provider.BlockedNumberContract;
import android.provider.CalendarContract;
@@ -1372,7 +1376,8 @@
data.startRequestedElapsedTime = startRequestedElapsedTime;
data.startRequestedUptime = startRequestedUptime;
updateCompatOverrideScale(compatInfo);
- CompatibilityInfo.applyOverrideScaleIfNeeded(config);
+ updateCompatOverrideDisplayRotation(compatInfo);
+ CompatibilityInfo.applyOverrideIfNeeded(config);
sendMessage(H.BIND_APPLICATION, data);
}
@@ -1386,6 +1391,15 @@
}
}
+ private void updateCompatOverrideDisplayRotation(@NonNull CompatibilityInfo info) {
+ if (info.isOverrideDisplayRotationRequired()) {
+ CompatibilityInfo.setOverrideDisplayRotation(info.applicationDisplayRotation);
+ } else {
+ CompatibilityInfo.setOverrideDisplayRotation(
+ WindowConfiguration.ROTATION_UNDEFINED);
+ }
+ }
+
public final void runIsolatedEntryPoint(String entryPoint, String[] entryPointArgs) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = entryPoint;
@@ -2036,6 +2050,7 @@
ucd.pkg = pkg;
ucd.info = info;
updateCompatOverrideScale(info);
+ updateCompatOverrideDisplayRotation(info);
sendMessage(H.UPDATE_PACKAGE_COMPATIBILITY_INFO, ucd);
}
@@ -2225,6 +2240,29 @@
args.arg6 = uiTranslationSpec;
sendMessage(H.UPDATE_UI_TRANSLATION_STATE, args);
}
+
+ @Override
+ public void getExecutableMethodFileOffsets(
+ @NonNull MethodDescriptor methodDescriptor,
+ @NonNull IOffsetCallback resultCallback) {
+ Method method = MethodDescriptorParser.parseMethodDescriptor(
+ getClass().getClassLoader(), methodDescriptor);
+ VMDebug.ExecutableMethodFileOffsets location =
+ VMDebug.getExecutableMethodFileOffsets(method);
+ try {
+ if (location == null) {
+ resultCallback.onResult(null);
+ return;
+ }
+ ExecutableMethodFileOffsets ret = new ExecutableMethodFileOffsets();
+ ret.containerPath = location.getContainerPath();
+ ret.containerOffset = location.getContainerOffset();
+ ret.methodOffset = location.getMethodOffset();
+ resultCallback.onResult(ret);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
private @NonNull SafeCancellationTransport createSafeCancellationTransport(
@@ -3907,12 +3945,7 @@
if (mLastProcessState == processState) {
return;
}
- // Do not issue a transitional GC if we are transitioning between 2 cached states.
- // Only update if the state flips between cached and uncached or vice versa
- if (ActivityManager.isProcStateCached(mLastProcessState)
- != ActivityManager.isProcStateCached(processState)) {
- updateVmProcessState(processState);
- }
+ updateVmProcessState(mLastProcessState, processState);
mLastProcessState = processState;
if (localLOGV) {
Slog.i(TAG, "******************* PROCESS STATE CHANGED TO: " + processState
@@ -3921,18 +3954,21 @@
}
}
+ /** Converts a process state to a VM process state. */
+ private static int toVmProcessState(int processState) {
+ final int state = ActivityManager.isProcStateJankPerceptible(processState)
+ ? VM_PROCESS_STATE_JANK_PERCEPTIBLE
+ : VM_PROCESS_STATE_JANK_IMPERCEPTIBLE;
+ return state;
+ }
+
/** Update VM state based on ActivityManager.PROCESS_STATE_* constants. */
- // Currently ART VM only uses state updates for Transitional GC, and thus
- // this function initiates a Transitional GC for transitions into Cached apps states.
- private void updateVmProcessState(int processState) {
- // Only a transition into Cached state should result in a Transitional GC request
- // to the ART runtime. Update VM state to JANK_IMPERCEPTIBLE in that case.
- // Note that there are 4 possible cached states currently, all of which are
- // JANK_IMPERCEPTIBLE from GC point of view.
- final int state = ActivityManager.isProcStateCached(processState)
- ? VM_PROCESS_STATE_JANK_IMPERCEPTIBLE
- : VM_PROCESS_STATE_JANK_PERCEPTIBLE;
- VMRuntime.getRuntime().updateProcessState(state);
+ private void updateVmProcessState(int lastProcessState, int newProcessState) {
+ final int state = toVmProcessState(newProcessState);
+ if (lastProcessState == PROCESS_STATE_UNKNOWN
+ || state != toVmProcessState(lastProcessState)) {
+ VMRuntime.getRuntime().updateProcessState(state);
+ }
}
@Override
diff --git a/core/java/android/app/AppOpsManager.aidl b/core/java/android/app/AppOpsManager.aidl
index 56ed290..b4dee2e 100644
--- a/core/java/android/app/AppOpsManager.aidl
+++ b/core/java/android/app/AppOpsManager.aidl
@@ -19,7 +19,6 @@
parcelable AppOpsManager.PackageOps;
parcelable AppOpsManager.NoteOpEventProxyInfo;
parcelable AppOpsManager.NoteOpEvent;
-parcelable AppOpsManager.NotedOp;
parcelable AppOpsManager.OpFeatureEntry;
parcelable AppOpsManager.OpEntry;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index c789e28..1913812 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -262,24 +262,6 @@
private static final Object sLock = new Object();
- // A map that records noted times for each op.
- private final ArrayMap<NotedOp, Integer> mPendingNotedOps = new ArrayMap<>();
- private final HandlerThread mHandlerThread;
- private final Handler mHandler;
- private static final int NOTE_OP_BATCHING_DELAY_MILLIS = 1000;
-
- private boolean isNoteOpBatchingSupported() {
- // If noteOp is called from system server no IPC is made, hence we don't need batching.
- if (Process.myUid() == Process.SYSTEM_UID) {
- return false;
- }
- return Flags.noteOpBatchingEnabled();
- }
-
- private final Object mBatchedNoteOpLock = new Object();
- @GuardedBy("mBatchedNoteOpLock")
- private boolean mIsBatchedNoteOpCallScheduled = false;
-
/** Current {@link OnOpNotedCallback}. Change via {@link #setOnOpNotedCallback} */
@GuardedBy("sLock")
private static @Nullable OnOpNotedCallback sOnOpNotedCallback;
@@ -7484,135 +7466,6 @@
}
/**
- * A NotedOp is an app op grouped in noteOp API and sent to the system server in a batch
- *
- * @hide
- */
- public static final class NotedOp implements Parcelable {
- private final @IntRange(from = 0, to = _NUM_OP - 1) int mOp;
- private final @IntRange(from = 0) int mUid;
- private final @Nullable String mPackageName;
- private final @Nullable String mAttributionTag;
- private final int mVirtualDeviceId;
- private final @Nullable String mMessage;
- private final boolean mShouldCollectAsyncNotedOp;
- private final boolean mShouldCollectMessage;
-
- public NotedOp(int op, int uid, @Nullable String packageName,
- @Nullable String attributionTag, int virtualDeviceId, @Nullable String message,
- boolean shouldCollectAsyncNotedOp, boolean shouldCollectMessage) {
- mOp = op;
- mUid = uid;
- mPackageName = packageName;
- mAttributionTag = attributionTag;
- mVirtualDeviceId = virtualDeviceId;
- mMessage = message;
- mShouldCollectAsyncNotedOp = shouldCollectAsyncNotedOp;
- mShouldCollectMessage = shouldCollectMessage;
- }
-
- NotedOp(Parcel source) {
- mOp = source.readInt();
- mUid = source.readInt();
- mPackageName = source.readString();
- mAttributionTag = source.readString();
- mVirtualDeviceId = source.readInt();
- mMessage = source.readString();
- mShouldCollectAsyncNotedOp = source.readBoolean();
- mShouldCollectMessage = source.readBoolean();
- }
-
- public int getOp() {
- return mOp;
- }
-
- public int getUid() {
- return mUid;
- }
-
- public @Nullable String getPackageName() {
- return mPackageName;
- }
-
- public @Nullable String getAttributionTag() {
- return mAttributionTag;
- }
-
- public int getVirtualDeviceId() {
- return mVirtualDeviceId;
- }
-
- public @Nullable String getMessage() {
- return mMessage;
- }
-
- public boolean getShouldCollectAsyncNotedOp() {
- return mShouldCollectAsyncNotedOp;
- }
-
- public boolean getShouldCollectMessage() {
- return mShouldCollectMessage;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mOp);
- dest.writeInt(mUid);
- dest.writeString(mPackageName);
- dest.writeString(mAttributionTag);
- dest.writeInt(mVirtualDeviceId);
- dest.writeString(mMessage);
- dest.writeBoolean(mShouldCollectAsyncNotedOp);
- dest.writeBoolean(mShouldCollectMessage);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- NotedOp that = (NotedOp) o;
- return mOp == that.mOp && mUid == that.mUid && Objects.equals(mPackageName,
- that.mPackageName) && Objects.equals(mAttributionTag, that.mAttributionTag)
- && mVirtualDeviceId == that.mVirtualDeviceId && Objects.equals(mMessage,
- that.mMessage) && Objects.equals(mShouldCollectAsyncNotedOp,
- that.mShouldCollectAsyncNotedOp) && Objects.equals(mShouldCollectMessage,
- that.mShouldCollectMessage);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mOp, mUid, mPackageName, mAttributionTag, mVirtualDeviceId,
- mMessage, mShouldCollectAsyncNotedOp, mShouldCollectMessage);
- }
-
- @Override
- public String toString() {
- return "NotedOp{" + "mOp=" + mOp + ", mUid=" + mUid + ", mPackageName=" + mPackageName
- + ", mAttributionTag=" + mAttributionTag + ", mVirtualDeviceId="
- + mVirtualDeviceId + ", mMessage=" + mMessage + ", mShouldCollectAsyncNotedOp="
- + mShouldCollectAsyncNotedOp + ", mShouldCollectMessage="
- + mShouldCollectMessage + "}";
- }
-
-
- public static final @NonNull Creator<NotedOp> CREATOR =
- new Creator<>() {
- @Override public NotedOp createFromParcel(Parcel source) {
- return new NotedOp(source);
- }
-
- @Override public NotedOp[] newArray(int size) {
- return new NotedOp[size];
- }
- };
- }
-
- /**
* Computes the sum of the counts for the given flags in between the begin and
* end UID states.
*
@@ -8126,9 +7979,6 @@
AppOpsManager(Context context, IAppOpsService service) {
mContext = context;
mService = service;
- mHandlerThread = new HandlerThread("AppOpsManager");
- mHandlerThread.start();
- mHandler = mHandlerThread.getThreadHandler();
if (mContext != null) {
final PackageManager pm = mContext.getPackageManager();
@@ -9465,74 +9315,15 @@
}
}
- SyncNotedAppOp syncOp = null;
- boolean skipBinderCall = false;
- if (isNoteOpBatchingSupported()) {
- int mode = sAppOpModeCache.query(
- new AppOpModeQuery(op, uid, packageName, virtualDeviceId, attributionTag,
- "noteOpNoThrow"));
- // For FOREGROUND mode, we still need to make a binder call to the system service
- // to translate it to ALLOWED or IGNORED. So no batching is needed.
- if (mode != MODE_FOREGROUND) {
- synchronized (mBatchedNoteOpLock) {
- NotedOp notedOp = new NotedOp(op, uid, packageName, attributionTag,
- virtualDeviceId, message, collectionMode == COLLECT_ASYNC,
- shouldCollectMessage);
-
- // Batch same noteOp calls and send them with their counters to the system
- // service asynchronously. The time window for batching is specified in
- // NOTE_OP_BATCHING_DELAY_MILLIS. Always allow the first noteOp call to go
- // through binder API. Accumulate subsequent same noteOp calls during the
- // time window in mPendingNotedOps.
- if (!mPendingNotedOps.containsKey(notedOp)) {
- mPendingNotedOps.put(notedOp, 0);
- } else {
- skipBinderCall = true;
- mPendingNotedOps.merge(notedOp, 1, Integer::sum);
- }
-
- if (!mIsBatchedNoteOpCallScheduled) {
- mHandler.postDelayed(() -> {
- ArrayMap<NotedOp, Integer> pendingNotedOpsCopy;
- synchronized(mBatchedNoteOpLock) {
- mIsBatchedNoteOpCallScheduled = false;
- pendingNotedOpsCopy =
- new ArrayMap<NotedOp, Integer>(mPendingNotedOps);
- mPendingNotedOps.clear();
- }
- for (int i = pendingNotedOpsCopy.size() - 1; i >= 0; i--) {
- if (pendingNotedOpsCopy.valueAt(i) == 0) {
- pendingNotedOpsCopy.removeAt(i);
- }
- }
- if (!pendingNotedOpsCopy.isEmpty()) {
- try {
- mService.noteOperationsInBatch(pendingNotedOpsCopy);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }, NOTE_OP_BATCHING_DELAY_MILLIS);
-
- mIsBatchedNoteOpCallScheduled = true;
- }
- }
-
- syncOp = new SyncNotedAppOp(mode, op, attributionTag, packageName);
- }
+ SyncNotedAppOp syncOp;
+ if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
+ syncOp = mService.noteOperation(op, uid, packageName, attributionTag,
+ collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
+ } else {
+ syncOp = mService.noteOperationForDevice(op, uid, packageName, attributionTag,
+ virtualDeviceId, collectionMode == COLLECT_ASYNC, message,
+ shouldCollectMessage);
}
-
- if (!skipBinderCall) {
- if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
- syncOp = mService.noteOperation(op, uid, packageName, attributionTag,
- collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
- } else {
- syncOp = mService.noteOperationForDevice(op, uid, packageName, attributionTag,
- virtualDeviceId, collectionMode == COLLECT_ASYNC, message,
- shouldCollectMessage);
- }
- }
-
if (syncOp.getOpMode() == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
collectNotedOpForSelf(syncOp);
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index 8b7ea0f..b21defb 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -29,7 +29,7 @@
import com.android.internal.util.function.DodecFunction;
import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
-import com.android.internal.util.function.NonaFunction;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.UndecFunction;
@@ -86,9 +86,9 @@
*/
SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
@Nullable String featureId, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
- @Nullable String message, boolean shouldCollectMessage, int notedCount,
- @NonNull NonaFunction<Integer, Integer, String, String, Integer, Boolean, String,
- Boolean, Integer, SyncNotedAppOp> superImpl);
+ @Nullable String message, boolean shouldCollectMessage,
+ @NonNull OctFunction<Integer, Integer, String, String, Integer, Boolean, String,
+ Boolean, SyncNotedAppOp> superImpl);
/**
* Allows overriding note proxy operation behavior.
diff --git a/core/java/android/app/BroadcastStickyCache.java b/core/java/android/app/BroadcastStickyCache.java
index fe2e107..0b6cf59 100644
--- a/core/java/android/app/BroadcastStickyCache.java
+++ b/core/java/android/app/BroadcastStickyCache.java
@@ -35,6 +35,7 @@
import android.util.ArrayMap;
import android.view.WindowManagerPolicyConstants;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
@@ -71,8 +72,10 @@
@VisibleForTesting
public static final ArrayMap<String, String> sActionApiNameMap = new ArrayMap<>();
+ @GuardedBy("BroadcastStickyCache.class")
private static final ArrayMap<String, IpcDataCache.Config> sActionConfigMap = new ArrayMap<>();
+ @GuardedBy("BroadcastStickyCache.class")
private static final ArrayMap<StickyBroadcastFilter, IpcDataCache<Void, Intent>>
sFilterCacheMap = new ArrayMap<>();
@@ -154,37 +157,44 @@
@Nullable String broadcastPermission,
@UserIdInt int userId,
@RegisterReceiverFlags int flags) {
- IpcDataCache<Void, Intent> intentDataCache = findIpcDataCache(filter);
+ IpcDataCache<Void, Intent> intentDataCache;
- if (intentDataCache == null) {
- final String action = filter.getAction(0);
- final StickyBroadcastFilter stickyBroadcastFilter =
- new StickyBroadcastFilter(filter, action);
- final Config config = getConfig(action);
+ synchronized (BroadcastStickyCache.class) {
+ intentDataCache = findIpcDataCache(filter);
- intentDataCache =
- new IpcDataCache<>(config,
- (query) -> ActivityManager.getService().registerReceiverWithFeature(
- applicationThread,
- mBasePackageName,
- attributionTag,
- /* receiverId= */ "null",
- /* receiver= */ null,
- filter,
- broadcastPermission,
- userId,
- flags));
- sFilterCacheMap.put(stickyBroadcastFilter, intentDataCache);
+ if (intentDataCache == null) {
+ final String action = filter.getAction(0);
+ final StickyBroadcastFilter stickyBroadcastFilter =
+ new StickyBroadcastFilter(filter, action);
+ final Config config = getConfig(action);
+
+ intentDataCache =
+ new IpcDataCache<>(config,
+ (query) -> ActivityManager.getService().registerReceiverWithFeature(
+ applicationThread,
+ mBasePackageName,
+ attributionTag,
+ /* receiverId= */ "null",
+ /* receiver= */ null,
+ filter,
+ broadcastPermission,
+ userId,
+ flags));
+ sFilterCacheMap.put(stickyBroadcastFilter, intentDataCache);
+ }
}
return intentDataCache.query(null);
}
@VisibleForTesting
public static void clearCacheForTest() {
- sFilterCacheMap.clear();
+ synchronized (BroadcastStickyCache.class) {
+ sFilterCacheMap.clear();
+ }
}
@Nullable
+ @GuardedBy("BroadcastStickyCache.class")
private static IpcDataCache<Void, Intent> findIpcDataCache(
@NonNull IntentFilter filter) {
for (int i = sFilterCacheMap.size() - 1; i >= 0; i--) {
@@ -198,6 +208,7 @@
}
@NonNull
+ @GuardedBy("BroadcastStickyCache.class")
private static IpcDataCache.Config getConfig(@NonNull String action) {
if (!sActionConfigMap.containsKey(action)) {
// We only need 1 entry per cache but just to be on the safer side we are choosing 32
diff --git a/core/java/android/app/CameraCompatTaskInfo.java b/core/java/android/app/CameraCompatTaskInfo.java
index 432a0da..845d2ac 100644
--- a/core/java/android/app/CameraCompatTaskInfo.java
+++ b/core/java/android/app/CameraCompatTaskInfo.java
@@ -16,11 +16,16 @@
package android.app;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.Surface;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -153,6 +158,27 @@
+ "}";
}
+ /**
+ * Returns the sandboxed display rotation based on the given {@code cameraCompatMode}.
+ *
+ * <p>This will be what the app likely expects in its requested orientation while running on a
+ * device with portrait natural orientation: `CAMERA_COMPAT_FREEFORM_PORTRAIT_*` is 0, and
+ * `CAMERA_COMPAT_FREEFORM_LANDSCAPE_*` is 90.
+ *
+ * @return {@link WindowConfiguration#ROTATION_UNDEFINED} if not in camera compat mode.
+ */
+ @Surface.Rotation
+ public static int getDisplayRotationFromCameraCompatMode(@FreeformCameraCompatMode int
+ cameraCompatMode) {
+ return switch (cameraCompatMode) {
+ case CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE,
+ CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_PORTRAIT -> ROTATION_0;
+ case CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_LANDSCAPE,
+ CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_PORTRAIT -> ROTATION_90;
+ default -> ROTATION_UNDEFINED;
+ };
+ }
+
/** Human readable version of the freeform camera compat mode. */
@NonNull
public static String freeformCameraCompatModeToString(
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 06d01ec..063501b 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -46,6 +46,8 @@
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.SharedMemory;
+import android.os.instrumentation.IOffsetCallback;
+import android.os.instrumentation.MethodDescriptor;
import android.view.autofill.AutofillId;
import android.view.translation.TranslationSpec;
import android.view.translation.UiTranslationSpec;
@@ -183,4 +185,6 @@
void scheduleTimeoutService(IBinder token, int startId);
void scheduleTimeoutServiceForType(IBinder token, int startId, int fgsType);
void schedulePing(in RemoteCallback pong);
+ void getExecutableMethodFileOffsets(in MethodDescriptor methodDescriptor,
+ in IOffsetCallback resultCallback);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index a4d8a5c..7e998d9 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3221,7 +3221,6 @@
/**
* @hide
*/
- @FlaggedApi(Flags.FLAG_UI_RICH_ONGOING)
public boolean containsCustomViews() {
return contentView != null
|| bigContentView != null
@@ -3235,7 +3234,6 @@
/**
* @hide
*/
- @FlaggedApi(Flags.FLAG_UI_RICH_ONGOING)
public boolean hasTitle() {
return extras != null
&& (!TextUtils.isEmpty(extras.getCharSequence(EXTRA_TITLE))
@@ -3245,7 +3243,7 @@
/**
* @hide
*/
- @FlaggedApi(Flags.FLAG_UI_RICH_ONGOING)
+ @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
public boolean hasPromotableStyle() {
final Class<? extends Style> notificationStyle = getNotificationStyle();
@@ -3257,11 +3255,16 @@
}
/**
- * @hide
+ * Returns whether the notification has all the characteristics that make it eligible for
+ * {@link #FLAG_PROMOTED_ONGOING}. This method does not factor in other criteria such user
+ * preferences for the app or channel. If this returns true, it does not guarantee that the
+ * notification will be assigned FLAG_PROMOTED_ONGOING by the system, but if this returns false,
+ * it will not.
*/
- @FlaggedApi(Flags.FLAG_UI_RICH_ONGOING)
+ @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
public boolean hasPromotableCharacteristics() {
return isColorizedRequested()
+ && isOngoingEvent()
&& hasTitle()
&& !isGroupSummary()
&& !containsCustomViews()
@@ -4158,6 +4161,13 @@
/**
* @hide
*/
+ public boolean isOngoingEvent() {
+ return (flags & FLAG_ONGOING_EVENT) != 0;
+ }
+
+ /**
+ * @hide
+ */
public boolean hasCompletedProgress() {
// not a progress notification; can't be complete
if (!extras.containsKey(EXTRA_PROGRESS)
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 3973c58..1e971a5 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -80,6 +80,17 @@
@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PropertyInvalidatedCache<Query, Result> {
/**
+ * A method to report if the PermissionManager notifications can be separated from cache
+ * invalidation. The feature relies on a series of flags; the dependency is captured in this
+ * method.
+ * @hide
+ */
+ public static boolean separatePermissionNotificationsEnabled() {
+ return isSharedMemoryAvailable()
+ && Flags.picSeparatePermissionNotifications();
+ }
+
+ /**
* This is a configuration class that customizes a cache instance.
* @hide
*/
@@ -1283,6 +1294,13 @@
public static record Args(@NonNull String mModule, @Nullable String mApi,
int mMaxEntries, boolean mIsolateUids, boolean mTestMode, boolean mCacheNulls) {
+ /**
+ * Default values for fields.
+ */
+ public static final int DEFAULT_MAX_ENTRIES = 32;
+ public static final boolean DEFAULT_ISOLATE_UIDS = true;
+ public static final boolean DEFAULT_CACHE_NULLS = false;
+
// Validation: the module must be one of the known module strings and the maxEntries must
// be positive.
public Args {
@@ -1297,10 +1315,10 @@
public Args(@NonNull String module) {
this(module,
null, // api
- 32, // maxEntries
- true, // isolateUids
+ DEFAULT_MAX_ENTRIES,
+ DEFAULT_ISOLATE_UIDS,
false, // testMode
- true // allowNulls
+ DEFAULT_CACHE_NULLS
);
}
@@ -1350,7 +1368,7 @@
* Burst a property name into module and api. Throw if the key is invalid. This method is
* used in to transition legacy cache constructors to the args constructor.
*/
- private static Args parseProperty(@NonNull String name) {
+ private static Args argsFromProperty(@NonNull String name) {
throwIfInvalidCacheKey(name);
// Strip off the leading well-known prefix.
String base = name.substring(CACHE_KEY_PREFIX.length() + 1);
@@ -1373,8 +1391,9 @@
*
* @hide
*/
+ @Deprecated
public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName) {
- this(parseProperty(propertyName).maxEntries(maxEntries), propertyName, null);
+ this(argsFromProperty(propertyName).maxEntries(maxEntries), propertyName, null);
}
/**
@@ -1388,9 +1407,10 @@
* @param cacheName Name of this cache in debug and dumpsys
* @hide
*/
+ @Deprecated
public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName,
@NonNull String cacheName) {
- this(parseProperty(propertyName).maxEntries(maxEntries), cacheName, null);
+ this(argsFromProperty(propertyName).maxEntries(maxEntries), cacheName, null);
}
/**
@@ -1846,6 +1866,14 @@
}
/**
+ * Invalidate caches in all processes that have the module and api specified in the args.
+ * @hide
+ */
+ public static void invalidateCache(@NonNull Args args) {
+ invalidateCache(createPropertyName(args.mModule, args.mApi));
+ }
+
+ /**
* Invalidate PropertyInvalidatedCache caches in all processes that are keyed on
* {@var name}. This function is synchronous: caches are invalidated upon return.
*
@@ -1921,6 +1949,10 @@
}
public AutoCorker(@NonNull String propertyName, int autoCorkDelayMs) {
+ if (separatePermissionNotificationsEnabled()) {
+ throw new IllegalStateException("AutoCorking is unavailable");
+ }
+
mPropertyName = propertyName;
mAutoCorkDelayMs = autoCorkDelayMs;
// We can't initialize mHandler here: when we're created, the main loop might not
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 3cffca7..51d0b18 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -1517,10 +1517,8 @@
int changes = mResConfiguration.updateFrom(config);
if (compat != null && (mResCompatibilityInfo == null
|| !mResCompatibilityInfo.equals(compat))) {
+ changes |= compat.getCompatibilityChangesForConfig(mResCompatibilityInfo);
mResCompatibilityInfo = compat;
- changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
- | ActivityInfo.CONFIG_SCREEN_SIZE
- | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
}
// If a application info update was scheduled to occur in this process but has not
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 2bd2d34..248e0433 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -42,8 +42,7 @@
import android.app.contextualsearch.ContextualSearchManager;
import android.app.ecm.EnhancedConfirmationFrameworkInitializer;
import android.app.job.JobSchedulerFrameworkInitializer;
-import android.app.ondeviceintelligence.IOnDeviceIntelligenceManager;
-import android.app.ondeviceintelligence.OnDeviceIntelligenceManager;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceFrameworkInitializer;
import android.app.people.PeopleManager;
import android.app.prediction.AppPredictionManager;
import android.app.role.RoleFrameworkInitializer;
@@ -82,8 +81,6 @@
import android.content.Context;
import android.content.IRestrictionsManager;
import android.content.RestrictionsManager;
-import android.content.integrity.AppIntegrityManager;
-import android.content.integrity.IAppIntegrityManager;
import android.content.om.IOverlayManager;
import android.content.om.OverlayManager;
import android.content.pm.ApplicationInfo;
@@ -1581,16 +1578,6 @@
return new AttestationVerificationManager(ctx.getOuterContext(),
IAttestationVerificationManagerService.Stub.asInterface(b));
}});
-
- //CHECKSTYLE:ON IndentationCheck
- registerService(Context.APP_INTEGRITY_SERVICE, AppIntegrityManager.class,
- new CachedServiceFetcher<AppIntegrityManager>() {
- @Override
- public AppIntegrityManager createService(ContextImpl ctx)
- throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(Context.APP_INTEGRITY_SERVICE);
- return new AppIntegrityManager(IAppIntegrityManager.Stub.asInterface(b));
- }});
registerService(Context.APP_HIBERNATION_SERVICE, AppHibernationManager.class,
new CachedServiceFetcher<AppHibernationManager>() {
@Override
@@ -1704,19 +1691,6 @@
throw new ServiceNotFoundException(Context.WEARABLE_SENSING_SERVICE);
}});
- registerService(Context.ON_DEVICE_INTELLIGENCE_SERVICE, OnDeviceIntelligenceManager.class,
- new CachedServiceFetcher<OnDeviceIntelligenceManager>() {
- @Override
- public OnDeviceIntelligenceManager createService(ContextImpl ctx)
- throws ServiceNotFoundException {
- IBinder iBinder = ServiceManager.getServiceOrThrow(
- Context.ON_DEVICE_INTELLIGENCE_SERVICE);
- IOnDeviceIntelligenceManager manager =
- IOnDeviceIntelligenceManager.Stub.asInterface(iBinder);
- return new OnDeviceIntelligenceManager(ctx.getOuterContext(), manager);
- }
- });
-
registerService(Context.GRAMMATICAL_INFLECTION_SERVICE, GrammaticalInflectionManager.class,
new CachedServiceFetcher<GrammaticalInflectionManager>() {
@Override
@@ -1861,6 +1835,7 @@
ConnectivityFrameworkInitializerTiramisu.registerServiceWrappers();
NearbyFrameworkInitializer.registerServiceWrappers();
OnDevicePersonalizationFrameworkInitializer.registerServiceWrappers();
+ OnDeviceIntelligenceFrameworkInitializer.registerServiceWrappers();
DeviceLockFrameworkInitializer.registerServiceWrappers();
VirtualizationFrameworkInitializer.registerServiceWrappers();
ConnectivityFrameworkInitializerBaklava.registerServiceWrappers();
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 01cc9d8..76705dc 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -308,12 +308,18 @@
public boolean isSleeping;
/**
- * Whether the top activity fillsParent() is false
+ * Whether the top activity fillsParent() is false.
* @hide
*/
public boolean isTopActivityTransparent;
/**
+ * Whether fillsParent() is false for every activity in the tasks stack.
+ * @hide
+ */
+ public boolean isActivityStackTransparent;
+
+ /**
* The last non-fullscreen bounds the task was launched in or resized to.
* @hide
*/
@@ -489,6 +495,7 @@
&& parentTaskId == that.parentTaskId
&& Objects.equals(topActivity, that.topActivity)
&& isTopActivityTransparent == that.isTopActivityTransparent
+ && isActivityStackTransparent == that.isActivityStackTransparent
&& Objects.equals(lastNonFullscreenBounds, that.lastNonFullscreenBounds)
&& Objects.equals(capturedLink, that.capturedLink)
&& capturedLinkTimestamp == that.capturedLinkTimestamp
@@ -567,6 +574,7 @@
mTopActivityLocusId = source.readTypedObject(LocusId.CREATOR);
displayAreaFeatureId = source.readInt();
isTopActivityTransparent = source.readBoolean();
+ isActivityStackTransparent = source.readBoolean();
lastNonFullscreenBounds = source.readTypedObject(Rect.CREATOR);
capturedLink = source.readTypedObject(Uri.CREATOR);
capturedLinkTimestamp = source.readLong();
@@ -623,6 +631,7 @@
dest.writeTypedObject(mTopActivityLocusId, flags);
dest.writeInt(displayAreaFeatureId);
dest.writeBoolean(isTopActivityTransparent);
+ dest.writeBoolean(isActivityStackTransparent);
dest.writeTypedObject(lastNonFullscreenBounds, flags);
dest.writeTypedObject(capturedLink, flags);
dest.writeLong(capturedLinkTimestamp);
@@ -668,6 +677,7 @@
+ " locusId=" + mTopActivityLocusId
+ " displayAreaFeatureId=" + displayAreaFeatureId
+ " isTopActivityTransparent=" + isTopActivityTransparent
+ + " isActivityStackTransparent=" + isActivityStackTransparent
+ " lastNonFullscreenBounds=" + lastNonFullscreenBounds
+ " capturedLink=" + capturedLink
+ " capturedLinkTimestamp=" + capturedLinkTimestamp
diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig
index 44940ae..720e045 100644
--- a/core/java/android/app/activity_manager.aconfig
+++ b/core/java/android/app/activity_manager.aconfig
@@ -53,16 +53,6 @@
flag {
namespace: "backstage_power"
- name: "gate_fgs_timeout_anr_behavior"
- description: "Gate the new behavior where an ANR is thrown once an FGS times out."
- bug: "339315145"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- namespace: "backstage_power"
name: "enable_fgs_timeout_crash_behavior"
description: "Enable the new behavior where the app is crashed once an FGS times out."
bug: "339526947"
@@ -166,3 +156,10 @@
bug: "362537357"
is_exported: true
}
+
+flag {
+ name: "jank_perceptible_narrow"
+ namespace: "system_performance"
+ description: "Narrow the scope of Jank Perceptible"
+ bug: "304837972"
+}
diff --git a/core/java/android/app/admin/DevicePolicyIdentifiers.java b/core/java/android/app/admin/DevicePolicyIdentifiers.java
index 35149b5..89261a4 100644
--- a/core/java/android/app/admin/DevicePolicyIdentifiers.java
+++ b/core/java/android/app/admin/DevicePolicyIdentifiers.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.appfunctions.flags.Flags;
import android.os.UserManager;
import java.util.Objects;
@@ -181,6 +182,12 @@
public static final String CONTENT_PROTECTION_POLICY = "contentProtection";
/**
+ * String identifier for {@link DevicePolicyManager#setAppFunctionsPolicy(int)}.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ public static final String APP_FUNCTIONS_POLICY = "appFunctions";
+
+ /**
* String identifier for {@link DevicePolicyManager#setUsbDataSignalingEnabled}.
*/
public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 8372078..9ddc729 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -23,6 +23,7 @@
import static android.Manifest.permission.MANAGE_DEVICE_ADMINS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_FUNCTIONS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CAMERA;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CERTIFICATES;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE;
@@ -3931,6 +3932,11 @@
@FlaggedApi(android.view.contentprotection.flags.Flags.FLAG_MANAGE_DEVICE_POLICY_ENABLED)
public static final int OPERATION_SET_CONTENT_PROTECTION_POLICY = 41;
+ /** @hide */
+ @TestApi
+ @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ public static final int OPERATION_SET_APP_FUNCTIONS_POLICY = 42;
+
private static final String PREFIX_OPERATION = "OPERATION_";
/** @hide */
@@ -3975,7 +3981,8 @@
OPERATION_SET_PERMISSION_POLICY,
OPERATION_SET_RESTRICTIONS_PROVIDER,
OPERATION_UNINSTALL_CA_CERT,
- OPERATION_SET_CONTENT_PROTECTION_POLICY
+ OPERATION_SET_CONTENT_PROTECTION_POLICY,
+ OPERATION_SET_APP_FUNCTIONS_POLICY
})
@Retention(RetentionPolicy.SOURCE)
public static @interface DevicePolicyOperation {
@@ -4347,6 +4354,90 @@
}
/**
+ * Indicates that app functions are not controlled by policy.
+ *
+ * <p>If no admin set this policy, it means appfunctions are enabled.
+ */
+ @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ public static final int APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY = 0;
+
+ /** Indicates that app functions are controlled and disabled by a policy. */
+ @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ public static final int APP_FUNCTIONS_DISABLED = 1;
+
+ /**
+ * Indicates that app functions are controlled and disabled by a policy for cross profile
+ * interactions only.
+ *
+ * <p>This is different from {@link #APP_FUNCTIONS_DISABLED} in that it only disables cross
+ * profile interactions (even if the caller has permissions required to interact across users).
+ * appfunctions can still be used within the a user profile boundary.
+ */
+ @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ public static final int APP_FUNCTIONS_DISABLED_CROSS_PROFILE = 2;
+
+ /** @hide */
+ @IntDef(
+ prefix = {"APP_FUNCTIONS_"},
+ value = {
+ APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY,
+ APP_FUNCTIONS_DISABLED,
+ APP_FUNCTIONS_DISABLED_CROSS_PROFILE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AppFunctionsPolicy {}
+
+ /**
+ * Sets the app functions policy which controls app functions operations on the device.
+ *
+ * <p>This function can only be called by a device owner, a profile owner or holders of the
+ * permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_APP_FUNCTIONS}.
+ *
+ * @param policy The app functions policy to set. One of {@link
+ * #APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY},
+ * {@link #APP_FUNCTIONS_DISABLED} or
+ * {@link #APP_FUNCTIONS_DISABLED_CROSS_PROFILE}
+ * @throws SecurityException if caller is not a device owner, a profile owner or a holder
+ * of the permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_APP_FUNCTIONS}.
+ */
+ @RequiresPermission(value = MANAGE_DEVICE_POLICY_APP_FUNCTIONS, conditional = true)
+ @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ public void setAppFunctionsPolicy(@AppFunctionsPolicy int policy) {
+ throwIfParentInstance("setAppFunctionsPolicy");
+ if (mService != null) {
+ try {
+ mService.setAppFunctionsPolicy(mContext.getPackageName(), policy);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Returns the current app functions policy.
+ *
+ * <p>The returned policy will be the current resolved policy rather than the policy set by the
+ * calling admin.
+ *
+ * @throws SecurityException if caller is not a device owner, a profile owner or a holder
+ * of the permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_APP_FUNCTIONS}.
+ */
+ @RequiresPermission(value = MANAGE_DEVICE_POLICY_APP_FUNCTIONS, conditional = true)
+ @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ public @AppFunctionsPolicy int getAppFunctionsPolicy() {
+ throwIfParentInstance("getAppFunctionsPolicy");
+ if (mService != null) {
+ try {
+ return mService.getAppFunctionsPolicy(mContext.getPackageName(),
+ myUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY;
+ }
+
+ /**
* This object is a single place to tack on invalidation and disable calls. All
* binder caches in this class derive from this Config, so all can be invalidated or
* disabled through this Config.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 03a9f99..f304c1b 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -644,4 +644,7 @@
int getPolicySizeForAdmin(String callerPackageName, in EnforcingAdmin admin);
int getHeadlessDeviceOwnerMode(String callerPackageName);
+
+ void setAppFunctionsPolicy(String callerPackageName, int policy);
+ int getAppFunctionsPolicy(String callerPackageName, int userId);
}
diff --git a/core/java/android/app/admin/UnknownAuthority.java b/core/java/android/app/admin/UnknownAuthority.java
index 82dcf7e..bebffde 100644
--- a/core/java/android/app/admin/UnknownAuthority.java
+++ b/core/java/android/app/admin/UnknownAuthority.java
@@ -74,14 +74,14 @@
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
- if (o != null && getClass() == o.getClass()) return false;
+ if (o == null || getClass() != o.getClass()) return false;
UnknownAuthority other = (UnknownAuthority) o;
return Objects.equals(mName, other.mName);
}
@Override
public int hashCode() {
- return mName.hashCode();
+ return Objects.hashCode(mName);
}
@Override
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 361ba73..af035cb 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -343,16 +343,6 @@
}
flag {
- name: "dont_write_policy_definition"
- namespace: "enterprise"
- description: "Don't write redundant policy-definition-entry tags"
- bug: "335663055"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "active_admin_cleanup"
namespace: "enterprise"
description: "Remove ActiveAdmin from EnforcingAdmin and related cleanups"
diff --git a/core/java/android/app/appfunctions/AppFunctionException.java b/core/java/android/app/appfunctions/AppFunctionException.java
index c8d80d3..d8179c7 100644
--- a/core/java/android/app/appfunctions/AppFunctionException.java
+++ b/core/java/android/app/appfunctions/AppFunctionException.java
@@ -32,8 +32,8 @@
/**
* Represents an app function related error.
*
- * <p>This exception may include an {@link AppFunctionException#getExtras() Bundle}
- * containing additional error-specific metadata.
+ * <p>This exception may include an {@link AppFunctionException#getExtras() Bundle} containing
+ * additional error-specific metadata.
*
* <p>The AppFunction SDK can expose structured APIs by packing and unpacking this Bundle.
*/
@@ -85,6 +85,13 @@
public static final int ERROR_CANCELLED = 2001;
/**
+ * The operation was disallowed by enterprise policy.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
+ */
+ public static final int ERROR_ENTERPRISE_POLICY_DISALLOWED = 2002;
+
+ /**
* An unknown error occurred while processing the call in the AppFunctionService.
*
* <p>This error is thrown when the service is connected in the remote application but an
@@ -231,7 +238,8 @@
ERROR_SYSTEM_ERROR,
ERROR_INVALID_ARGUMENT,
ERROR_DISABLED,
- ERROR_CANCELLED
+ ERROR_CANCELLED,
+ ERROR_ENTERPRISE_POLICY_DISALLOWED
})
@Retention(RetentionPolicy.SOURCE)
public @interface ErrorCode {}
diff --git a/core/java/android/app/contextualsearch/flags.aconfig b/core/java/android/app/contextualsearch/flags.aconfig
index 529b59a..e8cfd79 100644
--- a/core/java/android/app/contextualsearch/flags.aconfig
+++ b/core/java/android/app/contextualsearch/flags.aconfig
@@ -17,14 +17,14 @@
flag {
name: "multi_window_screen_context"
- namespace: "machine_learning"
+ namespace: "sysui_integrations"
description: "Report screen context and positions for all windows."
bug: "371065456"
}
flag {
name: "contextual_search_window_layer"
- namespace: "machine_learning"
+ namespace: "sysui_integrations"
description: "Identify live contextual search UI to exclude from contextual search screenshot."
bug: "372510690"
}
\ No newline at end of file
diff --git a/core/java/android/app/jank/JankDataProcessor.java b/core/java/android/app/jank/JankDataProcessor.java
index 7ceaeb3..c947259 100644
--- a/core/java/android/app/jank/JankDataProcessor.java
+++ b/core/java/android/app/jank/JankDataProcessor.java
@@ -215,7 +215,8 @@
try {
mPendingJankStats.values().forEach(stat -> {
- FrameworkStatsLog.write(FrameworkStatsLog.JANK_FRAME_COUNT_BY_WIDGET,
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.JANK_FRAME_COUNT_BY_WIDGET_REPORTED,
/*app uid*/ stat.getUid(),
/*activity name*/ stat.getActivityName(),
/*widget id*/ stat.getWidgetId(),
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 8b6840c..7543fa9 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -284,6 +284,13 @@
}
flag {
+ name: "nm_binder_perf_cache_channels"
+ namespace: "systemui"
+ description: "Use IpcDataCache for notification channel/group lookups"
+ bug: "362981561"
+}
+
+flag {
name: "no_sbnholder"
namespace: "systemui"
description: "removes sbnholder from NLS"
diff --git a/core/java/android/app/ondeviceintelligence/InferenceInfo.aidl b/core/java/android/app/ondeviceintelligence/InferenceInfo.aidl
deleted file mode 100644
index 6d70fc4..0000000
--- a/core/java/android/app/ondeviceintelligence/InferenceInfo.aidl
+++ /dev/null
@@ -1,22 +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.app.ondeviceintelligence;
-
-/**
- * @hide
- */
-parcelable InferenceInfo;
diff --git a/core/java/android/app/ondeviceintelligence/OWNERS b/core/java/android/app/ondeviceintelligence/OWNERS
deleted file mode 100644
index 85e9e65..0000000
--- a/core/java/android/app/ondeviceintelligence/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-# Bug component: 1363385
-
-sandeepbandaru@google.com
-shivanker@google.com
-hackz@google.com
-volnov@google.com
diff --git a/core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig b/core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig
deleted file mode 100644
index 74a96c8..0000000
--- a/core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig
+++ /dev/null
@@ -1,17 +0,0 @@
-package: "android.app.ondeviceintelligence.flags"
-container: "system"
-
-flag {
- name: "enable_on_device_intelligence"
- is_exported: true
- namespace: "ondeviceintelligence"
- description: "Make methods on OnDeviceIntelligenceManager available for local inference."
- bug: "304755128"
-}
-flag {
- name: "enable_on_device_intelligence_module"
- is_exported: true
- namespace: "ondeviceintelligence"
- description: "Enable migration to mainline module and related changes."
- bug: "376427781"
-}
\ No newline at end of file
diff --git a/core/java/android/app/performance.aconfig b/core/java/android/app/performance.aconfig
index 359c84e..238f1cb 100644
--- a/core/java/android/app/performance.aconfig
+++ b/core/java/android/app/performance.aconfig
@@ -37,6 +37,14 @@
flag {
namespace: "system_performance"
+ name: "pic_separate_permission_notifications"
+ is_fixed_read_only: true
+ description: "Seperate PermissionManager notifications from cache udpates"
+ bug: "379699402"
+}
+
+flag {
+ namespace: "system_performance"
name: "pic_cache_nulls"
is_fixed_read_only: true
description: "Cache null returns from binder calls"
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index 2b52681..75ecabd 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -56,7 +56,7 @@
@Override
public void preExecute(@NonNull ClientTransactionHandler client) {
- CompatibilityInfo.applyOverrideScaleIfNeeded(mConfiguration);
+ CompatibilityInfo.applyOverrideIfNeeded(mConfiguration);
// Notify the client of an upcoming change in the token configuration. This ensures that
// batches of config change items only process the newest configuration.
client.updatePendingActivityConfiguration(getActivityToken(), mConfiguration);
diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
index cecf701..bb88190 100644
--- a/core/java/android/app/servertransaction/ActivityRelaunchItem.java
+++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
@@ -89,7 +89,7 @@
public void preExecute(@NonNull ClientTransactionHandler client) {
// The local config is already scaled so only apply if this item is from server side.
if (!client.isExecutingLocalTransaction()) {
- CompatibilityInfo.applyOverrideScaleIfNeeded(mConfig);
+ CompatibilityInfo.applyOverrideIfNeeded(mConfig);
}
mActivityClientRecord = client.prepareRelaunchActivity(getActivityToken(), mPendingResults,
mPendingNewIntents, mConfigChanges, mConfig, mPreserveWindow, mActivityWindowInfo);
diff --git a/core/java/android/app/servertransaction/ConfigurationChangeItem.java b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
index 123d792..e42005b 100644
--- a/core/java/android/app/servertransaction/ConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
@@ -46,7 +46,7 @@
@Override
public void preExecute(@NonNull ClientTransactionHandler client) {
- CompatibilityInfo.applyOverrideScaleIfNeeded(mConfiguration);
+ CompatibilityInfo.applyOverrideIfNeeded(mConfiguration);
client.updatePendingConfiguration(mConfiguration);
}
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 235a9f7..f2e7a4f 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -202,8 +202,8 @@
public void preExecute(@NonNull ClientTransactionHandler client) {
client.countLaunchingActivities(1);
client.updateProcessState(mProcState, false);
- CompatibilityInfo.applyOverrideScaleIfNeeded(mCurConfig);
- CompatibilityInfo.applyOverrideScaleIfNeeded(mOverrideConfig);
+ CompatibilityInfo.applyOverrideIfNeeded(mCurConfig);
+ CompatibilityInfo.applyOverrideIfNeeded(mOverrideConfig);
client.updatePendingConfiguration(mCurConfig);
if (mActivityClientController != null) {
ActivityClient.setActivityClientController(mActivityClientController);
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index 1aa563a..72d1f49 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -58,7 +58,7 @@
@Override
public void preExecute(@NonNull ClientTransactionHandler client) {
- CompatibilityInfo.applyOverrideScaleIfNeeded(mConfiguration);
+ CompatibilityInfo.applyOverrideIfNeeded(mConfiguration);
// Notify the client of an upcoming change in the token configuration. This ensures that
// batches of config change items only process the newest configuration.
client.updatePendingActivityConfiguration(getActivityToken(), mConfiguration);
diff --git a/core/java/android/app/supervision/SupervisionManagerInternal.java b/core/java/android/app/supervision/SupervisionManagerInternal.java
index d571e14..2cf6ae6 100644
--- a/core/java/android/app/supervision/SupervisionManagerInternal.java
+++ b/core/java/android/app/supervision/SupervisionManagerInternal.java
@@ -27,32 +27,41 @@
*/
public abstract class SupervisionManagerInternal {
/**
- * Returns whether supervision is enabled for the specified user
+ * Returns whether the app with given process uid is the active supervision app.
*
- * @param userId The user to retrieve the supervision state for
- * @return whether the user is supervised
+ * <p>Supervision app is considered active when supervision is enabled for the user running the
+ * given process uid.
+ *
+ * @param uid App process uid.
+ * @return Whether the app is the active supervision app.
+ */
+ public abstract boolean isActiveSupervisionApp(int uid);
+
+ /**
+ * Returns whether supervision is enabled for the specified user.
+ *
+ * @param userId The user to retrieve the supervision state for.
+ * @return Whether the user is supervised.
*/
public abstract boolean isSupervisionEnabledForUser(@UserIdInt int userId);
- /**
- * Returns whether the supervision lock screen needs to be shown.
- */
+ /** Returns whether the supervision lock screen needs to be shown. */
public abstract boolean isSupervisionLockscreenEnabledForUser(@UserIdInt int userId);
/**
* Set whether supervision is enabled for the specified user.
*
- * @param userId The user to set the supervision state for
- * @param enabled Whether or not the user should be supervised
+ * @param userId The user to set the supervision state for.
+ * @param enabled Whether or not the user should be supervised.
*/
public abstract void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled);
/**
- * Sets whether the supervision lock screen should be shown for the specified user
+ * Sets whether the supervision lock screen should be shown for the specified user.
*
- * @param userId The user set the superivision state for
- * @param enabled Whether or not the superivision lock screen needs to be shown
- * @param options Optional configuration parameters for the supervision lock screen
+ * @param userId The user set the superivision state for.
+ * @param enabled Whether or not the superivision lock screen needs to be shown.
+ * @param options Optional configuration parameters for the supervision lock screen.
*/
public abstract void setSupervisionLockscreenEnabledForUser(
@UserIdInt int userId, boolean enabled, @Nullable PersistableBundle options);
diff --git a/core/java/android/app/supervision/flags.aconfig b/core/java/android/app/supervision/flags.aconfig
index d4f82f6..1b03532 100644
--- a/core/java/android/app/supervision/flags.aconfig
+++ b/core/java/android/app/supervision/flags.aconfig
@@ -24,3 +24,11 @@
description: "Flag that enables supervision when the supervision app is the profile owner"
bug: "377261590"
}
+
+flag {
+ name: "deprecate_dpm_supervision_apis"
+ is_exported: true
+ namespace: "supervision"
+ description: "Flag that deprecates supervision methods in DPM"
+ bug: "382034839"
+}
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 40de298..67ad459 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -37,36 +37,44 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.Intent.FilterComparison;
import android.content.IntentSender;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.Pair;
import android.widget.RemoteViews;
import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.FunctionalUtils;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Updates AppWidget state; gets information about installed AppWidget providers and other
@@ -592,6 +600,8 @@
private boolean mHasPostedLegacyLists = false;
+ private @NonNull ServiceCollectionCache mServiceCollectionCache;
+
/**
* Get the AppWidgetManager instance to use for the supplied {@link android.content.Context
* Context} object.
@@ -612,6 +622,7 @@
mPackageName = context.getOpPackageName();
mService = service;
mDisplayMetrics = context.getResources().getDisplayMetrics();
+ mServiceCollectionCache = new ServiceCollectionCache(context, /* timeout= */ 5000L);
if (mService == null) {
return;
}
@@ -649,7 +660,7 @@
final RemoteViews viewsCopy = new RemoteViews(original);
Runnable updateWidgetWithTask = () -> {
try {
- viewsCopy.collectAllIntents(mMaxBitmapMemory).get();
+ viewsCopy.collectAllIntents(mMaxBitmapMemory, mServiceCollectionCache).get();
action.acceptOrThrow(viewsCopy);
} catch (Exception e) {
Log.e(TAG, failureMsg, e);
@@ -1629,4 +1640,106 @@
thread.start();
return thread.getThreadHandler();
}
+
+ /**
+ * @hide
+ */
+ public static class ServiceCollectionCache {
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final long mTimeOut;
+
+ private final Map<FilterComparison, ConnectionTask> mActiveConnections =
+ new ArrayMap<>();
+
+ public ServiceCollectionCache(Context context, long timeOut) {
+ mContext = context;
+ mHandler = new Handler(BackgroundThread.getHandler().getLooper());
+ mTimeOut = timeOut;
+ }
+
+ /**
+ * Connect to the service indicated by the {@code Intent}, and consume the binder on the
+ * specified executor
+ */
+ public void connectAndConsume(Intent intent, Consumer<IBinder> task, Executor executor) {
+ mHandler.post(() -> connectAndConsumeInner(intent, task, executor));
+ }
+
+ private void connectAndConsumeInner(Intent intent, Consumer<IBinder> task,
+ Executor executor) {
+ ConnectionTask activeConnection = mActiveConnections.computeIfAbsent(
+ new FilterComparison(intent), ConnectionTask::new);
+ activeConnection.add(task, executor);
+ }
+
+ private class ConnectionTask implements ServiceConnection {
+
+ private final Runnable mDestroyAfterTimeout = this::onDestroyTimeout;
+ private final ArrayDeque<Pair<Consumer<IBinder>, Executor>> mTaskQueue =
+ new ArrayDeque<>();
+
+ private boolean mOnDestroyTimeout = false;
+ private IBinder mIBinder;
+
+ ConnectionTask(@NonNull FilterComparison filter) {
+ mContext.bindService(filter.getIntent(),
+ Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE),
+ mHandler::post,
+ this);
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+ mIBinder = iBinder;
+ mHandler.post(this::handleNext);
+ }
+
+ @Override
+ public void onNullBinding(ComponentName name) {
+ // Use an empty binder, follow up tasks will handle the failure
+ onServiceConnected(name, new Binder());
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName componentName) { }
+
+ void add(Consumer<IBinder> task, Executor executor) {
+ mTaskQueue.add(Pair.create(task, executor));
+ if (mOnDestroyTimeout) {
+ // If we are waiting for timeout, cancel it and execute the next task
+ handleNext();
+ }
+ }
+
+ private void handleNext() {
+ mHandler.removeCallbacks(mDestroyAfterTimeout);
+ Pair<Consumer<IBinder>, Executor> next = mTaskQueue.pollFirst();
+ if (next != null) {
+ mOnDestroyTimeout = false;
+ next.second.execute(() -> {
+ next.first.accept(mIBinder);
+ mHandler.post(this::handleNext);
+ });
+ } else {
+ // Finished all tasks, start a timeout to unbind this service
+ mOnDestroyTimeout = true;
+ mHandler.postDelayed(mDestroyAfterTimeout, mTimeOut);
+ }
+ }
+
+ /**
+ * Called after we have waited for {@link #mTimeOut} after the last task is finished
+ */
+ private void onDestroyTimeout() {
+ if (!mTaskQueue.isEmpty()) {
+ handleNext();
+ return;
+ }
+ mContext.unbindService(this);
+ mActiveConnections.values().remove(this);
+ }
+ }
+ }
}
diff --git a/core/java/android/companion/DeviceId.java b/core/java/android/companion/DeviceId.java
index f66a1ae..2f19eb4 100644
--- a/core/java/android/companion/DeviceId.java
+++ b/core/java/android/companion/DeviceId.java
@@ -22,7 +22,6 @@
import android.net.MacAddress;
import android.os.Parcel;
import android.os.Parcelable;
-import android.provider.OneTimeUseBuilder;
import java.util.Locale;
import java.util.Objects;
@@ -154,8 +153,12 @@
/**
* A builder for {@link DeviceId}
+ *
+ * <p>Calling apps must provide at least one of the following to identify
+ * the device: a custom ID using {@link #setCustomId(String)}, or a MAC address using
+ * {@link #setMacAddress(MacAddress)}.</p>
*/
- public static final class Builder extends OneTimeUseBuilder<DeviceId> {
+ public static final class Builder {
private String mCustomId;
private MacAddress mMacAddress;
@@ -171,7 +174,6 @@
*/
@NonNull
public Builder setCustomId(@Nullable String customId) {
- checkNotUsed();
if (customId != null
&& customId.length() > CUSTOM_ID_LENGTH_LIMIT) {
throw new IllegalArgumentException("Length of the custom id must be at most "
@@ -191,15 +193,12 @@
*/
@NonNull
public Builder setMacAddress(@Nullable MacAddress macAddress) {
- checkNotUsed();
mMacAddress = macAddress;
return this;
}
@NonNull
- @Override
public DeviceId build() {
- markUsed();
if (mCustomId == null && mMacAddress == null) {
throw new IllegalArgumentException("At least one device id property must be"
+ "non-null to build a DeviceId.");
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index a577ec0..a6492d3 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -55,6 +55,7 @@
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.net.Uri;
+import android.os.BadParcelableException;
import android.os.Build;
import android.os.Bundle;
import android.os.BundleMerger;
@@ -7857,6 +7858,10 @@
*/
public static final int URI_ALLOW_UNSAFE = 1<<2;
+ static {
+ Bundle.intentClass = Intent.class;
+ }
+
// ---------------------------------------------------------------------
private String mAction;
@@ -12398,8 +12403,19 @@
addExtendedFlags(EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED);
if (mExtras != null && !mExtras.isEmpty()) {
for (String key : mExtras.keySet()) {
- Object value = mExtras.get(key);
-
+ Object value;
+ try {
+ value = mExtras.get(key);
+ } catch (BadParcelableException e) {
+ // This could happen when the key points to a LazyValue whose class cannot be
+ // found by the classLoader - A nested object more than 1 level deeper who is
+ // of type of a custom class could trigger this situation. In such case, we
+ // ignore it since it is not an intent. However, it could be a custom type that
+ // extends from Intent. If such an object is retrieved later in another
+ // component, then trying to launch such a custom class object will fail unless
+ // removeLaunchSecurityProtection() is called before it is launched.
+ value = null;
+ }
if (value instanceof Intent intent && !visited.contains(intent)) {
handleNestedIntent(intent, visited, new NestedIntentKey(
NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL, key, 0));
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a06eb1c..7e08051 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -11651,7 +11651,7 @@
private static final PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo>
sApplicationInfoCache =
new PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo>(
- 2048, PermissionManager.CACHE_KEY_PACKAGE_INFO,
+ 2048, PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE,
"getApplicationInfo") {
@Override
public ApplicationInfo recompute(ApplicationInfoQuery query) {
@@ -11682,18 +11682,6 @@
sApplicationInfoCache.disableLocal();
}
- private static final PropertyInvalidatedCache.AutoCorker sCacheAutoCorker =
- new PropertyInvalidatedCache.AutoCorker(PermissionManager.CACHE_KEY_PACKAGE_INFO);
-
- /**
- * Invalidate caches of package and permission information system-wide.
- *
- * @hide
- */
- public static void invalidatePackageInfoCache() {
- sCacheAutoCorker.autoCork();
- }
-
// Some of the flags don't affect the query result, but let's be conservative and cache
// each combination of flags separately.
@@ -11752,7 +11740,7 @@
private static final PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>
sPackageInfoCache =
new PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>(
- 2048, PermissionManager.CACHE_KEY_PACKAGE_INFO,
+ 2048, PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE,
"getPackageInfo") {
@Override
public PackageInfo recompute(PackageInfoQuery query) {
@@ -11784,17 +11772,40 @@
/**
* Inhibit package info cache invalidations when correct.
*
- * @hide */
+ * @hide
+ */
public static void corkPackageInfoCache() {
- PropertyInvalidatedCache.corkInvalidations(PermissionManager.CACHE_KEY_PACKAGE_INFO);
+ sPackageInfoCache.corkInvalidations();
}
/**
* Enable package info cache invalidations.
*
- * @hide */
+ * @hide
+ */
public static void uncorkPackageInfoCache() {
- PropertyInvalidatedCache.uncorkInvalidations(PermissionManager.CACHE_KEY_PACKAGE_INFO);
+ sPackageInfoCache.uncorkInvalidations();
+ }
+
+ // This auto-corker is obsolete once the separate permission notifications feature is
+ // committed.
+ private static final PropertyInvalidatedCache.AutoCorker sCacheAutoCorker =
+ PropertyInvalidatedCache.separatePermissionNotificationsEnabled()
+ ? null
+ : new PropertyInvalidatedCache
+ .AutoCorker(PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE);
+
+ /**
+ * Invalidate caches of package and permission information system-wide.
+ *
+ * @hide
+ */
+ public static void invalidatePackageInfoCache() {
+ if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
+ sPackageInfoCache.invalidateCache();
+ } else {
+ sCacheAutoCorker.autoCork();
+ }
}
/**
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index 44f2a4c..23f1ff8 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -158,6 +158,17 @@
]
},
{
+ "name": "CtsPackageInstallerCUJUpdateOwnerShipTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJUpdateSelfTestCases",
"options":[
{
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index d6620d1..afcdcb0 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -17,7 +17,9 @@
package android.content.res;
import android.annotation.Nullable;
+import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.graphics.Canvas;
import android.graphics.Insets;
@@ -34,14 +36,17 @@
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.MotionEvent;
+import android.view.Surface;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
+import com.android.aconfig.annotations.VisibleForTesting;
+
/**
* CompatibilityInfo class keeps the information about the screen compatibility mode that the
* application is running under.
- *
- * {@hide}
+ *
+ * {@hide}
*/
@RavenwoodKeepWholeClass
public class CompatibilityInfo implements Parcelable {
@@ -129,12 +134,37 @@
*/
public final float applicationDensityInvertedScale;
+ /**
+ * Application's display rotation.
+ *
+ * <p>This field is used to sandbox fixed-orientation activities on displays or display areas
+ * with ignoreOrientationRequest, where the display orientation is more likely to be different
+ * from the orientation the activity requested (e.g. in desktop windowing, or letterboxed).
+ * Mainly set for activities which use the display rotation to orient their content, for example
+ * camera previews.
+ *
+ * <p>In the case of camera activities, assuming the wrong posture
+ * can lead to sideways or stretched previews. As part of camera compat treatment for desktop
+ * windowing, the app is sandboxed to believe that the app and the device are in the posture the
+ * app requested. For example for portrait fixed-orientation apps, the app is letterboxed to
+ * portrait, camera feed is cropped to portrait, and the display rotation is changed via this
+ * field, for example to {@link Surface.Rotation#ROTATION_0} on devices with portrait natural
+ * orientation. All of these parameters factor in common calculations for setting up the camera
+ * preview.
+ */
+ @Surface.Rotation
+ public int applicationDisplayRotation = WindowConfiguration.ROTATION_UNDEFINED;
+
/** The process level override inverted scale. See {@link #HAS_OVERRIDE_SCALING}. */
private static float sOverrideInvertedScale = 1f;
/** The process level override inverted density scale. See {@link #HAS_OVERRIDE_SCALING}. */
private static float sOverrideDensityInvertScale = 1f;
+ /** The process level override display rotation. */
+ @Surface.Rotation
+ private static int sOverrideDisplayRotation = WindowConfiguration.ROTATION_UNDEFINED;
+
@UnsupportedAppUsage
@Deprecated
public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw,
@@ -346,11 +376,16 @@
return (mCompatibilityFlags & HAS_OVERRIDE_SCALING) != 0;
}
+ /** Returns {@code true} if {@link #sOverrideDisplayRotation} should be set. */
+ public boolean isOverrideDisplayRotationRequired() {
+ return applicationDisplayRotation != WindowConfiguration.ROTATION_UNDEFINED;
+ }
+
@UnsupportedAppUsage
public boolean supportsScreen() {
return (mCompatibilityFlags&NEEDS_SCREEN_COMPAT) == 0;
}
-
+
public boolean neverSupportsScreen() {
return (mCompatibilityFlags&ALWAYS_NEEDS_COMPAT) != 0;
}
@@ -618,6 +653,9 @@
}
public void applyToConfiguration(int displayDensity, Configuration inoutConfig) {
+ if (hasOverrideDisplayRotation()) {
+ applyDisplayRotationConfiguration(sOverrideDisplayRotation, inoutConfig);
+ }
if (hasOverrideScale()) return;
if (!supportsScreen()) {
// This is a larger screen device and the app is not
@@ -650,21 +688,42 @@
inoutConfig.windowConfiguration.scale(invertScale);
}
- /** @see #sOverrideInvertedScale */
- public static void applyOverrideScaleIfNeeded(Configuration config) {
- if (!hasOverrideScale()) return;
- scaleConfiguration(sOverrideInvertedScale, sOverrideDensityInvertScale, config);
+ /** Changes the WindowConfiguration display rotation for the given configuration. */
+ public static void applyDisplayRotationConfiguration(@Surface.Rotation int displayRotation,
+ Configuration inoutConfig) {
+ if (displayRotation != WindowConfiguration.ROTATION_UNDEFINED) {
+ inoutConfig.windowConfiguration.setDisplayRotation(displayRotation);
+ }
}
- /** @see #sOverrideInvertedScale */
- public static void applyOverrideScaleIfNeeded(MergedConfiguration mergedConfig) {
- if (!hasOverrideScale()) return;
- scaleConfiguration(sOverrideInvertedScale, sOverrideDensityInvertScale,
- mergedConfig.getGlobalConfiguration());
- scaleConfiguration(sOverrideInvertedScale, sOverrideDensityInvertScale,
- mergedConfig.getOverrideConfiguration());
- scaleConfiguration(sOverrideInvertedScale, sOverrideDensityInvertScale,
- mergedConfig.getMergedConfiguration());
+ /** @see #sOverrideInvertedScale and #sOverrideDisplayRotation. */
+ public static void applyOverrideIfNeeded(Configuration config) {
+ if (hasOverrideDisplayRotation()) {
+ applyDisplayRotationConfiguration(sOverrideDisplayRotation, config);
+ }
+ if (hasOverrideScale()) {
+ scaleConfiguration(sOverrideInvertedScale, sOverrideDensityInvertScale, config);
+ }
+ }
+
+ /** @see #sOverrideInvertedScale and #sOverrideDisplayRotation. */
+ public static void applyOverrideIfNeeded(MergedConfiguration mergedConfig) {
+ if (hasOverrideDisplayRotation()) {
+ applyDisplayRotationConfiguration(sOverrideDisplayRotation,
+ mergedConfig.getGlobalConfiguration());
+ applyDisplayRotationConfiguration(sOverrideDisplayRotation,
+ mergedConfig.getOverrideConfiguration());
+ applyDisplayRotationConfiguration(sOverrideDisplayRotation,
+ mergedConfig.getMergedConfiguration());
+ }
+ if (hasOverrideScale()) {
+ scaleConfiguration(sOverrideInvertedScale, sOverrideDensityInvertScale,
+ mergedConfig.getGlobalConfiguration());
+ scaleConfiguration(sOverrideInvertedScale, sOverrideDensityInvertScale,
+ mergedConfig.getOverrideConfiguration());
+ scaleConfiguration(sOverrideInvertedScale, sOverrideDensityInvertScale,
+ mergedConfig.getMergedConfiguration());
+ }
}
/** Returns {@code true} if this process is in a environment with override scale. */
@@ -693,6 +752,22 @@
return sOverrideDensityInvertScale;
}
+ /** Returns {@code true} if this process is in a environment with override display rotation. */
+ private static boolean hasOverrideDisplayRotation() {
+ return sOverrideDisplayRotation != WindowConfiguration.ROTATION_UNDEFINED;
+ }
+
+ /** @see #sOverrideInvertedScale */
+ public static void setOverrideDisplayRotation(@Surface.Rotation int displayRotation) {
+ sOverrideDisplayRotation = displayRotation;
+ }
+
+ /** @see #sOverrideDisplayRotation */
+ @VisibleForTesting
+ public static int getOverrideDisplayRotation() {
+ return sOverrideDisplayRotation;
+ }
+
/**
* Compute the frame Rect for applications runs under compatibility mode.
*
@@ -747,18 +822,50 @@
if (this == o) {
return true;
}
- try {
- CompatibilityInfo oc = (CompatibilityInfo)o;
- if (mCompatibilityFlags != oc.mCompatibilityFlags) return false;
- if (applicationDensity != oc.applicationDensity) return false;
- if (applicationScale != oc.applicationScale) return false;
- if (applicationInvertedScale != oc.applicationInvertedScale) return false;
- if (applicationDensityScale != oc.applicationDensityScale) return false;
- if (applicationDensityInvertedScale != oc.applicationDensityInvertedScale) return false;
- return true;
- } catch (ClassCastException e) {
+
+ if (!(o instanceof CompatibilityInfo oc)) {
return false;
}
+
+ if (!isCompatibilityFlagsEqual(oc)) return false;
+ if (!isScaleEqual(oc)) return false;
+ if (!isDisplayRotationEqual(oc)) return false;
+ return true;
+ }
+
+ /**
+ * Checks the difference between this and given {@link CompatibilityInfo} o, and returns the
+ * combination of {@link ActivityInfo}.CONFIG_* changes that this difference should trigger.
+ */
+ public int getCompatibilityChangesForConfig(@Nullable CompatibilityInfo o) {
+ int changes = 0;
+ if (!isDisplayRotationEqual(o)) {
+ changes |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
+ }
+ if (!isScaleEqual(o) || !isCompatibilityFlagsEqual(o)) {
+ changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
+ | ActivityInfo.CONFIG_SCREEN_SIZE
+ | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+ }
+ return changes;
+ }
+
+ private boolean isScaleEqual(@Nullable CompatibilityInfo oc) {
+ if (oc == null) return false;
+ if (applicationDensity != oc.applicationDensity) return false;
+ if (applicationScale != oc.applicationScale) return false;
+ if (applicationInvertedScale != oc.applicationInvertedScale) return false;
+ if (applicationDensityScale != oc.applicationDensityScale) return false;
+ if (applicationDensityInvertedScale != oc.applicationDensityInvertedScale) return false;
+ return true;
+ }
+
+ private boolean isDisplayRotationEqual(@Nullable CompatibilityInfo oc) {
+ return oc != null && oc.applicationDisplayRotation == applicationDisplayRotation;
+ }
+
+ private boolean isCompatibilityFlagsEqual(@Nullable CompatibilityInfo oc) {
+ return oc != null && oc.mCompatibilityFlags == mCompatibilityFlags;
}
@Override
@@ -778,6 +885,10 @@
sb.append(" overrideDensityInvScale=");
sb.append(applicationDensityInvertedScale);
}
+ if (isOverrideDisplayRotationRequired()) {
+ sb.append(" overrideDisplayRotation=");
+ sb.append(applicationDisplayRotation);
+ }
if (!supportsScreen()) {
sb.append(" resizing");
}
@@ -800,6 +911,7 @@
result = 31 * result + Float.floatToIntBits(applicationInvertedScale);
result = 31 * result + Float.floatToIntBits(applicationDensityScale);
result = 31 * result + Float.floatToIntBits(applicationDensityInvertedScale);
+ result = 31 * result + applicationDisplayRotation;
return result;
}
@@ -816,6 +928,7 @@
dest.writeFloat(applicationInvertedScale);
dest.writeFloat(applicationDensityScale);
dest.writeFloat(applicationDensityInvertedScale);
+ dest.writeInt(applicationDisplayRotation);
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -839,6 +952,7 @@
applicationInvertedScale = source.readFloat();
applicationDensityScale = source.readFloat();
applicationDensityInvertedScale = source.readFloat();
+ applicationDisplayRotation = source.readInt();
}
/**
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
index 6c35d10..d8d4e16 100644
--- a/core/java/android/credentials/flags.aconfig
+++ b/core/java/android/credentials/flags.aconfig
@@ -87,6 +87,16 @@
flag {
namespace: "credential_manager"
+ name: "framework_session_id_metric_bundle"
+ description: "Enables the session_id to be passed across to the UI logs"
+ bug: "379880133"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "credential_manager"
name: "clear_credentials_fix_enabled"
description: "Fixes bug in clearCredential API that causes indefinite suspension"
bug: "314926460"
@@ -104,3 +114,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "credential_manager"
+ name: "propagate_user_context_for_intent_creation"
+ description: "Propagates the user ID in which to find the right OEM UI component to launch"
+ bug: "373711451"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/hardware/contexthub/HubEndpoint.java b/core/java/android/hardware/contexthub/HubEndpoint.java
index 7efdd6d..1f12bbf 100644
--- a/core/java/android/hardware/contexthub/HubEndpoint.java
+++ b/core/java/android/hardware/contexthub/HubEndpoint.java
@@ -107,7 +107,7 @@
public void onSessionOpenRequest(
int sessionId,
HubEndpointInfo initiator,
- @Nullable HubServiceInfo serviceInfo)
+ @Nullable String serviceDescriptor)
throws RemoteException {
HubEndpointSession activeSession;
synchronized (mLock) {
@@ -128,16 +128,16 @@
processSessionOpenRequestResult(
sessionId,
initiator,
- serviceInfo,
+ serviceDescriptor,
mLifecycleCallback.onSessionOpenRequest(
- initiator, serviceInfo)));
+ initiator, serviceDescriptor)));
}
}
private void processSessionOpenRequestResult(
int sessionId,
HubEndpointInfo initiator,
- @Nullable HubServiceInfo serviceInfo,
+ @Nullable String serviceDescriptor,
HubEndpointSessionResult result) {
if (result == null) {
throw new IllegalArgumentException(
@@ -145,7 +145,7 @@
}
if (result.isAccepted()) {
- acceptSession(sessionId, initiator, serviceInfo);
+ acceptSession(sessionId, initiator, serviceDescriptor);
} else {
Log.i(
TAG,
@@ -162,7 +162,7 @@
private void acceptSession(
int sessionId,
HubEndpointInfo initiator,
- @Nullable HubServiceInfo serviceInfo) {
+ @Nullable String serviceDescriptor) {
if (mServiceToken == null || mAssignedHubEndpointInfo == null) {
// No longer registered?
return;
@@ -187,7 +187,7 @@
HubEndpoint.this,
mAssignedHubEndpointInfo,
initiator,
- serviceInfo);
+ serviceDescriptor);
try {
// oneway call to notify system service that the request is completed
mServiceToken.openSessionRequestComplete(sessionId);
@@ -334,7 +334,6 @@
@Nullable IHubEndpointMessageCallback endpointMessageCallback,
@NonNull Executor messageCallbackExecutor) {
mPendingHubEndpointInfo = pendingEndpointInfo;
-
mLifecycleCallback = endpointLifecycleCallback;
mLifecycleCallbackExecutor = lifecycleCallbackExecutor;
mMessageCallback = endpointMessageCallback;
@@ -387,7 +386,7 @@
}
/** @hide */
- public void openSession(HubEndpointInfo destinationInfo, @Nullable HubServiceInfo serviceInfo) {
+ public void openSession(HubEndpointInfo destinationInfo, @Nullable String serviceDescriptor) {
// TODO(b/378974199): Consider refactor these assertions
if (mServiceToken == null || mAssignedHubEndpointInfo == null) {
// No longer registered?
@@ -397,7 +396,7 @@
HubEndpointSession newSession;
try {
// Request system service to assign session id.
- int sessionId = mServiceToken.openSession(destinationInfo, serviceInfo);
+ int sessionId = mServiceToken.openSession(destinationInfo, serviceDescriptor);
// Save the newly created session
synchronized (mLock) {
@@ -407,7 +406,7 @@
HubEndpoint.this,
destinationInfo,
mAssignedHubEndpointInfo,
- serviceInfo);
+ serviceDescriptor);
mActiveSessions.put(sessionId, newSession);
}
} catch (RemoteException e) {
diff --git a/core/java/android/hardware/contexthub/HubEndpointSession.java b/core/java/android/hardware/contexthub/HubEndpointSession.java
index cf952cb..77f937e 100644
--- a/core/java/android/hardware/contexthub/HubEndpointSession.java
+++ b/core/java/android/hardware/contexthub/HubEndpointSession.java
@@ -19,6 +19,7 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.chre.flags.Flags;
import android.hardware.location.ContextHubTransaction;
@@ -43,7 +44,7 @@
@NonNull private final HubEndpoint mHubEndpoint;
@NonNull private final HubEndpointInfo mInitiator;
@NonNull private final HubEndpointInfo mDestination;
- @Nullable private final HubServiceInfo mServiceInfo;
+ @Nullable private final String mServiceDescriptor;
private final AtomicBoolean mIsClosed = new AtomicBoolean(true);
@@ -53,12 +54,12 @@
@NonNull HubEndpoint hubEndpoint,
@NonNull HubEndpointInfo destination,
@NonNull HubEndpointInfo initiator,
- @Nullable HubServiceInfo serviceInfo) {
+ @Nullable String serviceDescriptor) {
mId = id;
mHubEndpoint = hubEndpoint;
mDestination = destination;
mInitiator = initiator;
- mServiceInfo = serviceInfo;
+ mServiceDescriptor = serviceDescriptor;
}
/**
@@ -68,8 +69,11 @@
* @return For messages that does not require a response, the transaction will immediately
* complete. For messages that requires a response, the transaction will complete after
* receiving the response for the message.
+ * @throws SecurityException if the application doesn't have the right permissions to send this
+ * message.
*/
@NonNull
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public ContextHubTransaction<Void> sendMessage(@NonNull HubMessage message) {
if (mIsClosed.get()) {
throw new IllegalStateException("Session is already closed.");
@@ -120,6 +124,7 @@
* <p>When this function is invoked, the messaging associated with this session is invalidated.
* All futures messages targeted for this client are dropped.
*/
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public void close() {
if (!mIsClosed.getAndSet(true)) {
mCloseGuard.close();
@@ -128,8 +133,8 @@
}
/**
- * Get the {@link HubServiceInfo} associated with this session. Null value indicates that there
- * is no service associated to this session.
+ * Get the service descriptor associated with this session. Null value indicates that there is
+ * no service associated to this session.
*
* <p>For hub initiated sessions, the object was previously used in as an argument for open
* request in {@link IHubEndpointLifecycleCallback#onSessionOpenRequest}.
@@ -138,8 +143,8 @@
* android.hardware.location.ContextHubManager#openSession}
*/
@Nullable
- public HubServiceInfo getServiceInfo() {
- return mServiceInfo;
+ public String getServiceDescriptor() {
+ return mServiceDescriptor;
}
@Override
diff --git a/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl b/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl
index 1c98b4b..b76b227 100644
--- a/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl
+++ b/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl
@@ -34,11 +34,12 @@
* Request system service to open a session with a specific destination.
*
* @param destination A valid HubEndpointInfo representing the destination.
+ * @param serviceDescriptor An optional descriptor of the service to scope this session to.
*
* @throws IllegalArgumentException If the HubEndpointInfo is not valid.
* @throws IllegalStateException If there are too many opened sessions.
*/
- int openSession(in HubEndpointInfo destination, in @nullable HubServiceInfo serviceInfo);
+ int openSession(in HubEndpointInfo destination, in @nullable String serviceDescriptor);
/**
* Request system service to close a specific session
diff --git a/core/java/android/hardware/contexthub/IContextHubEndpointCallback.aidl b/core/java/android/hardware/contexthub/IContextHubEndpointCallback.aidl
index 1ae5fb9..63edda8 100644
--- a/core/java/android/hardware/contexthub/IContextHubEndpointCallback.aidl
+++ b/core/java/android/hardware/contexthub/IContextHubEndpointCallback.aidl
@@ -29,9 +29,9 @@
*
* @param sessionId An integer identifying the session, assigned by the initiator
* @param initiator HubEndpointInfo representing the requester
- * @param serviceInfo Nullable HubServiceInfo representing the service associated with this session
+ * @param serviceDescriptor Nullable string representing the service associated with this session
*/
- void onSessionOpenRequest(int sessionId, in HubEndpointInfo initiator, in @nullable HubServiceInfo serviceInfo);
+ void onSessionOpenRequest(int sessionId, in HubEndpointInfo initiator, in @nullable String serviceDescriptor);
/**
* Request from system service to close a specific session
diff --git a/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java b/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java
index fe449bb..698ed0a 100644
--- a/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java
+++ b/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java
@@ -34,12 +34,12 @@
* Called when an endpoint is requesting a session be opened with another endpoint.
*
* @param requester The {@link HubEndpointInfo} object representing the requester
- * @param serviceInfo The {@link HubServiceInfo} object representing the service associated with
- * this session. Null indicates that there is no service associated with this session.
+ * @param serviceDescriptor A string describing the service associated with this session. Null
+ * indicates that there is no service associated with this session.
*/
@NonNull
HubEndpointSessionResult onSessionOpenRequest(
- @NonNull HubEndpointInfo requester, @Nullable HubServiceInfo serviceInfo);
+ @NonNull HubEndpointInfo requester, @Nullable String serviceDescriptor);
/**
* Called when a communication session is opened and ready to be used.
diff --git a/core/java/android/hardware/display/DisplayTopology.java b/core/java/android/hardware/display/DisplayTopology.java
index 54d0dd0..211aeff 100644
--- a/core/java/android/hardware/display/DisplayTopology.java
+++ b/core/java/android/hardware/display/DisplayTopology.java
@@ -91,6 +91,15 @@
@VisibleForTesting
public DisplayTopology(TreeNode root, int primaryDisplayId) {
mRoot = root;
+ if (mRoot != null) {
+ // Set mRoot's position and offset to predictable values, just so we don't leak state
+ // from some previous arrangement the node was used in, or leak arbitrary values passed
+ // to the TreeNode constructor. The position and offset don't mean anything because
+ // mRoot doesn't have a parent.
+ mRoot.mPosition = POSITION_LEFT;
+ mRoot.mOffset = 0f;
+ }
+
mPrimaryDisplayId = primaryDisplayId;
}
@@ -422,6 +431,14 @@
}
}
}
+
+ // Sort children lists by display ID.
+ final Comparator<TreeNode> idComparator = (d1, d2) -> {
+ return Integer.compare(d1.mDisplayId, d2.mDisplayId);
+ };
+ for (TreeNode display : displays) {
+ display.mChildren.sort(idComparator);
+ }
}
/**
@@ -582,6 +599,15 @@
}
}
+ /** Returns the graph representation of the topology */
+ public DisplayTopologyGraph getGraph() {
+ // TODO(b/364907904): implement
+ return new DisplayTopologyGraph(mPrimaryDisplayId,
+ new DisplayTopologyGraph.DisplayNode[] { new DisplayTopologyGraph.DisplayNode(
+ mRoot == null ? Display.DEFAULT_DISPLAY : mRoot.mDisplayId,
+ new DisplayTopologyGraph.AdjacentDisplay[0])});
+ }
+
/**
* Tests whether two brightness float values are within a small enough tolerance
* of each other.
diff --git a/core/java/android/hardware/display/DisplayTopologyGraph.java b/core/java/android/hardware/display/DisplayTopologyGraph.java
new file mode 100644
index 0000000..938e6d1
--- /dev/null
+++ b/core/java/android/hardware/display/DisplayTopologyGraph.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package android.hardware.display;
+
+/**
+ * Graph of the displays in {@link android.hardware.display.DisplayTopology} tree.
+ *
+ * @hide
+ */
+public record DisplayTopologyGraph(int primaryDisplayId, DisplayNode[] displayNodes) {
+ /**
+ * Display in the topology
+ */
+ public record DisplayNode(
+ int displayId,
+ AdjacentDisplay[] adjacentDisplays) {}
+
+ /**
+ * Edge to adjacent display
+ */
+ public record AdjacentDisplay(
+ // The logical Id of this adjacent display
+ int displayId,
+ // Side of the other display which touches this adjacent display.
+ @DisplayTopology.TreeNode.Position
+ int position,
+ // How many px this display is shifted along the touchingSide, can be negative.
+ float offsetPx) {}
+}
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index 3f9317a..f8f7f5e 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -28,7 +28,6 @@
import static com.android.hardware.input.Flags.mouseReverseVerticalScrolling;
import static com.android.hardware.input.Flags.mouseSwapPrimaryButton;
import static com.android.hardware.input.Flags.touchpadSystemGestureDisable;
-import static com.android.hardware.input.Flags.touchpadTapDragging;
import static com.android.hardware.input.Flags.touchpadThreeFingerTapShortcut;
import static com.android.hardware.input.Flags.touchpadVisualizer;
import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
@@ -366,15 +365,6 @@
}
/**
- * Returns true if the feature flag for touchpad tap dragging is enabled.
- *
- * @hide
- */
- public static boolean isTouchpadTapDraggingFeatureFlagEnabled() {
- return touchpadTapDragging();
- }
-
- /**
* Returns true if the feature flag for disabling system gestures on touchpads is enabled.
*
* @hide
@@ -461,9 +451,6 @@
* @hide
*/
public static boolean useTouchpadTapDragging(@NonNull Context context) {
- if (!isTouchpadTapDraggingFeatureFlagEnabled()) {
- return false;
- }
return Settings.System.getIntForUser(context.getContentResolver(),
Settings.System.TOUCHPAD_TAP_DRAGGING, 0, UserHandle.USER_CURRENT) == 1;
}
@@ -480,9 +467,6 @@
*/
@RequiresPermission(Manifest.permission.WRITE_SETTINGS)
public static void setTouchpadTapDragging(@NonNull Context context, boolean enabled) {
- if (!isTouchpadTapDraggingFeatureFlagEnabled()) {
- return;
- }
Settings.System.putIntForUser(context.getContentResolver(),
Settings.System.TOUCHPAD_TAP_DRAGGING, enabled ? 1 : 0,
UserHandle.USER_CURRENT);
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java
index 711dc3a..af756b9 100644
--- a/core/java/android/hardware/input/KeyGestureEvent.java
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -43,6 +43,8 @@
private static final int LOG_EVENT_UNSPECIFIED =
FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__UNSPECIFIED;
+ // These values should not change and values should not be re-used as this data is persisted to
+ // long term storage and must be kept backwards compatible.
public static final int KEY_GESTURE_TYPE_UNSPECIFIED = 0;
public static final int KEY_GESTURE_TYPE_HOME = 1;
public static final int KEY_GESTURE_TYPE_RECENT_APPS = 2;
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index ebb6172..aaa78aa 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -37,13 +37,6 @@
flag {
namespace: "input_native"
- name: "touchpad_tap_dragging"
- description: "Offers a setting to enable touchpad tap dragging"
- bug: "321978150"
-}
-
-flag {
- namespace: "input_native"
name: "keyboard_glyph_map"
description: "Allows system to provide keyboard specific key drawables and shortcuts via config files"
bug: "345440920"
@@ -196,4 +189,11 @@
namespace: "wallet_integration"
description: "Adds new API in WindowManager class to check if the window can override the power key double tap behavior."
bug: "378736024"
- }
\ No newline at end of file
+ }
+
+flag {
+ name: "pointer_acceleration"
+ namespace: "input"
+ description: "Allows the user to disable pointer acceleration for mouse and touchpads."
+ bug: "349006858"
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 3a74130..d9888ad 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -841,6 +841,7 @@
* @param endpointId The identifier of the hub endpoint.
* @param callback The callback to be invoked.
* @param executor The executor to invoke the callback on.
+ * @throws UnsupportedOperationException If the operation is not supported.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
@@ -881,6 +882,7 @@
* @param callback The callback to be invoked.
* @param executor The executor to invoke the callback on.
* @throws IllegalArgumentException if the serviceDescriptor is empty.
+ * @throws UnsupportedOperationException If the operation is not supported.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
@@ -911,6 +913,7 @@
*
* @param callback The callback previously registered.
* @throws IllegalArgumentException If the callback was not previously registered.
+ * @throws UnsupportedOperationException If the operation is not supported.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
@@ -1309,16 +1312,18 @@
* ContextHubManager#registerEndpoint(HubEndpoint)}.
* @param destination {@link HubEndpointInfo} object that represents an endpoint from previous
* endpoint discovery results (e.g. from {@link ContextHubManager#findEndpoints(long)}).
- * @param serviceInfo {@link HubServiceInfo} object that describes the service associated with
- * this session. The information will be sent to the destination as part of open request.
+ * @param serviceDescriptor A string that describes the service associated with this session.
+ * The information will be sent to the destination as part of open request.
+ * @throws IllegalStateException if hubEndpoint was not successfully registered, or if there is
+ * insufficient capacity for creating a session.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
public void openSession(
@NonNull HubEndpoint hubEndpoint,
@NonNull HubEndpointInfo destination,
- @NonNull HubServiceInfo serviceInfo) {
- hubEndpoint.openSession(destination, serviceInfo);
+ @NonNull String serviceDescriptor) {
+ hubEndpoint.openSession(destination, serviceDescriptor);
}
/**
diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
index 42028f6..e5717ac 100644
--- a/core/java/android/hardware/radio/ProgramSelector.java
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -43,22 +43,20 @@
* <li>DAB channel info</li>
* </ui>
*
- * <p>The primary ID uniquely identifies a station and can be used for equality
- * check. The secondary IDs are supplementary and can speed up tuning process,
- * but the primary ID is sufficient (ie. after a full band scan).
- *
- * <p>Two selectors with different secondary IDs, but the same primary ID are
- * considered equal. In particular, secondary IDs vector may get updated for
+ * <p>Except for DAB radio, two selectors with different secondary IDs, but the same primary
+ * ID are considered equal. In particular, secondary IDs vector may get updated for
* an entry on the program list (ie. when a better frequency for a given
- * station is found).
+ * station is found). For DAB radio, two selectors with the same primary ID and the same
+ * DAB frequency and DAB ensemble secondary IDs (if exist) are considered equal.
*
* <p>The primaryId of a given programType MUST be of a specific type:
* <ui>
- * <li>AM, FM: RDS_PI if the station broadcasts RDS, AMFM_FREQUENCY otherwise;</li>
- * <li>AM_HD, FM_HD: HD_STATION_ID_EXT;</li>
- * <li>DAB: DAB_SIDECC;</li>
- * <li>DRMO: DRMO_SERVICE_ID;</li>
- * <li>SXM: SXM_SERVICE_ID;</li>
+ * <li>AM, FM: {@link #IDENTIFIER_TYPE_RDS_PI} if the station broadcasts RDS,
+ * {@link #IDENTIFIER_TYPE_AMFM_FREQUENCY} otherwise;</li>
+ * <li>AM_HD, FM_HD: {@link #IDENTIFIER_TYPE_HD_STATION_ID_EXT};</li>
+ * <li>DAB: {@link #IDENTIFIER_TYPE_DAB_SID_EXT} or
+ * {@link #IDENTIFIER_TYPE_DAB_DMB_SID_EXT};</li>
+ * <li>DRMO: {@link #IDENTIFIER_TYPE_DRMO_SERVICE_ID};</li>
* <li>VENDOR: VENDOR_PRIMARY.</li>
* </ui>
* @hide
@@ -597,9 +595,9 @@
* negatives. In particular, it may be way off for certain regions.
* The main purpose is to avoid passing improper units, ie. MHz instead of kHz.
*
- * @param isAm true, if AM, false if FM.
+ * @param isAm {@code true}, if AM, {@code false} if FM.
* @param frequencyKhz the frequency in kHz.
- * @return true, if the frequency is roughly valid.
+ * @return {@code true}, if the frequency is roughly valid.
*/
private static boolean isValidAmFmFrequency(boolean isAm, int frequencyKhz) {
if (isAm) {
@@ -785,8 +783,8 @@
* ProgramLists for category entries.
*
* @see ProgramList.Filter#areCategoriesIncluded
- * @return False if this identifier's type is not tunable (e.g. DAB ensemble or
- * vendor-specified type). True otherwise.
+ * @return {@link false} if this identifier's type is not tunable (e.g. DAB ensemble or
+ * vendor-specified type). {@link true} otherwise.
*/
public boolean isCategoryType() {
return (mType >= IDENTIFIER_TYPE_VENDOR_START && mType <= IDENTIFIER_TYPE_VENDOR_END)
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index 9b37533..9badbf8 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -299,9 +299,10 @@
if (event.hasNoModifiers()) {
return false;
}
- return event.hasModifiers(KeyEvent.META_CTRL_ON)
- || event.hasModifiers(KeyEvent.META_ALT_ON)
- || event.hasModifiers(KeyEvent.KEYCODE_FUNCTION);
+ return event.isCtrlPressed()
+ || event.isAltPressed()
+ || event.isFunctionPressed()
+ || event.isMetaPressed();
}
private boolean needsVerification(KeyEvent event) {
diff --git a/core/java/android/net/EventLogTags.logtags b/core/java/android/net/EventLogTags.logtags
index d5ed014..32953c9 100644
--- a/core/java/android/net/EventLogTags.logtags
+++ b/core/java/android/net/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package android.net
diff --git a/core/java/android/net/metrics/DnsEvent.java b/core/java/android/net/metrics/DnsEvent.java
index bf351ce..f53d1c4 100644
--- a/core/java/android/net/metrics/DnsEvent.java
+++ b/core/java/android/net/metrics/DnsEvent.java
@@ -62,7 +62,11 @@
return isSuccess;
}
if (eventCount == eventTypes.length) {
- resize((int) (1.4 * eventCount));
+ int resizeLength = (int) (1.4 * eventCount);
+ if (eventCount == resizeLength) {
+ resizeLength++;
+ }
+ resize(resizeLength);
}
eventTypes[eventCount] = eventType;
returnCodes[eventCount] = returnCode;
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 8b6da7e..84ca5ed 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -403,7 +403,7 @@
* increase when the hardware manufacturer provides an OTA update.
* <p>
* This constant records the major version of Android. Use {@link
- * SDK_INT_FULL} if you need to consider the minor version of Android
+ * #SDK_INT_FULL} if you need to consider the minor version of Android
* as well.
* <p>
* Possible values are defined in {@link Build.VERSION_CODES}.
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 05bd10b..819d58d 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -70,6 +70,11 @@
*/
static final int FLAG_VERIFY_TOKENS_PRESENT = 1 << 13;
+ /**
+ * Indicates the bundle definitely contains an Intent.
+ */
+ static final int FLAG_HAS_INTENT = 1 << 14;
+
/**
* Status when the Bundle can <b>assert</b> that the underlying Parcel DOES NOT contain
@@ -118,6 +123,11 @@
public static final Bundle EMPTY;
/**
+ * @hide
+ */
+ public static Class<?> intentClass;
+
+ /**
* Special extras used to denote extras have been stripped off.
* @hide
*/
@@ -388,6 +398,7 @@
if ((bundle.mFlags & FLAG_HAS_BINDERS_KNOWN) == 0) {
mFlags &= ~FLAG_HAS_BINDERS_KNOWN;
}
+ mFlags |= bundle.mFlags & FLAG_HAS_INTENT;
}
/**
@@ -447,6 +458,16 @@
}
}
+ /**
+ * Returns if the bundle definitely contains at least an intent. This method returns false does
+ * not guarantee the bundle does not contain a nested intent. An intent could still exist in a
+ * ParcelableArrayList, ParcelableArray, ParcelableList, a bundle in this bundle, etc.
+ * @hide
+ */
+ public boolean hasIntent() {
+ return (mFlags & FLAG_HAS_INTENT) != 0;
+ }
+
/** {@hide} */
@Override
public void putObject(@Nullable String key, @Nullable Object value) {
@@ -569,6 +590,9 @@
mMap.put(key, value);
mFlags &= ~FLAG_HAS_FDS_KNOWN;
mFlags &= ~FLAG_HAS_BINDERS_KNOWN;
+ if (intentClass != null && intentClass.isInstance(value)) {
+ mFlags |= FLAG_HAS_INTENT;
+ }
}
/**
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 23114c4..ce56a4f 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -18,18 +18,25 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
+import android.app.ActivityThread;
+import android.app.Instrumentation;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Process;
import android.os.UserHandle;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.ravenwood.annotation.RavenwoodRedirect;
import android.ravenwood.annotation.RavenwoodRedirectionClass;
+import android.ravenwood.annotation.RavenwoodReplace;
+import android.ravenwood.annotation.RavenwoodThrow;
import android.util.Log;
import android.util.Printer;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.ravenwood.RavenwoodEnvironment;
import dalvik.annotation.optimization.NeverCompile;
@@ -119,7 +126,7 @@
MessageQueue(boolean quitAllowed) {
initIsProcessAllowedToUseConcurrent();
- mUseConcurrent = sIsProcessAllowedToUseConcurrent;
+ mUseConcurrent = sIsProcessAllowedToUseConcurrent && !isInstrumenting();
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
@@ -172,6 +179,37 @@
}
}
+ @RavenwoodReplace
+ private static void throwIfNotTest() {
+ final ActivityThread activityThread = ActivityThread.currentActivityThread();
+ if (activityThread == null) {
+ // Only tests can reach here.
+ return;
+ }
+ final Instrumentation instrumentation = activityThread.getInstrumentation();
+ if (instrumentation == null) {
+ // Only tests can reach here.
+ return;
+ }
+ if (instrumentation.isInstrumenting()) {
+ return;
+ }
+ throw new IllegalStateException("Test-only API called not from a test!");
+ }
+
+ private static void throwIfNotTest$ravenwood() {
+ return;
+ }
+
+ private static boolean isInstrumenting() {
+ final ActivityThread activityThread = ActivityThread.currentActivityThread();
+ if (activityThread == null) {
+ return false;
+ }
+ final Instrumentation instrumentation = activityThread.getInstrumentation();
+ return instrumentation != null && instrumentation.isInstrumenting();
+ }
+
@Override
protected void finalize() throws Throwable {
try {
@@ -192,12 +230,9 @@
private static final class MatchDeliverableMessages extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
- if (m.when <= when) {
- return true;
- }
- return false;
+ return n.mMessage.when <= when;
}
}
private final MatchDeliverableMessages mMatchDeliverableMessages =
@@ -344,7 +379,7 @@
* @see OnFileDescriptorEventListener
* @see #removeOnFileDescriptorEventListener
*/
- @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
+ @RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd,
@OnFileDescriptorEventListener.Events int events,
@NonNull OnFileDescriptorEventListener listener) {
@@ -378,7 +413,7 @@
* @see OnFileDescriptorEventListener
* @see #addOnFileDescriptorEventListener
*/
- @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
+ @RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
public void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd) {
if (fd == null) {
throw new IllegalArgumentException("fd must not be null");
@@ -394,7 +429,7 @@
}
}
- @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
+ @RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events,
OnFileDescriptorEventListener listener) {
final int fdNum = fd.getInt$();
@@ -517,7 +552,7 @@
/* This is only read/written from the Looper thread. For use with Concurrent MQ */
private int mNextPollTimeoutMillis;
private boolean mMessageDirectlyQueued;
- private Message nextMessage() {
+ private Message nextMessage(boolean peek) {
int i = 0;
while (true) {
@@ -679,7 +714,7 @@
if (sState.compareAndSet(this, sStackStateActive, nextOp)) {
mMessageCounts.clearCounts();
if (found != null) {
- if (!removeFromPriorityQueue(found)) {
+ if (!peek && !removeFromPriorityQueue(found)) {
/*
* RemoveMessages() might be able to pull messages out from under us
* However we can detect that here and just loop around if it happens.
@@ -713,7 +748,7 @@
mMessageDirectlyQueued = false;
nativePollOnce(ptr, mNextPollTimeoutMillis);
- Message msg = nextMessage();
+ Message msg = nextMessage(false);
if (msg != null) {
msg.markInUse();
return msg;
@@ -1032,8 +1067,9 @@
}
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
+ final Message m = n.mMessage;
if (m.target == null && m.arg1 == mBarrierToken) {
return true;
}
@@ -1236,10 +1272,155 @@
return true;
}
+ private Message legacyPeekOrPop(boolean peek) {
+ synchronized (this) {
+ // Try to retrieve the next message. Return if found.
+ final long now = SystemClock.uptimeMillis();
+ Message prevMsg = null;
+ Message msg = mMessages;
+ if (msg != null && msg.target == null) {
+ // Stalled by a barrier. Find the next asynchronous message in the queue.
+ do {
+ prevMsg = msg;
+ msg = msg.next;
+ } while (msg != null && !msg.isAsynchronous());
+ }
+ if (msg != null) {
+ if (now >= msg.when) {
+ // Got a message.
+ mBlocked = false;
+ if (peek) {
+ return msg;
+ }
+ if (prevMsg != null) {
+ prevMsg.next = msg.next;
+ if (prevMsg.next == null) {
+ mLast = prevMsg;
+ }
+ } else {
+ mMessages = msg.next;
+ if (msg.next == null) {
+ mLast = null;
+ }
+ }
+ msg.next = null;
+ msg.markInUse();
+ if (msg.isAsynchronous()) {
+ mAsyncMessageCount--;
+ }
+ if (TRACE) {
+ Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet());
+ }
+ return msg;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the timestamp of the next executable message in our priority queue.
+ * Returns null if there are no messages ready for delivery.
+ *
+ * Caller must ensure that this doesn't race 'next' from the Looper thread.
+ */
+ @SuppressLint("VisiblySynchronized") // Legacy MessageQueue synchronizes on this
+ Long peekWhenForTest() {
+ throwIfNotTest();
+ Message ret;
+ if (mUseConcurrent) {
+ ret = nextMessage(true);
+ } else {
+ ret = legacyPeekOrPop(true);
+ }
+ return ret != null ? ret.when : null;
+ }
+
+ /**
+ * Return the next executable message in our priority queue.
+ * Returns null if there are no messages ready for delivery
+ *
+ * Caller must ensure that this doesn't race 'next' from the Looper thread.
+ */
+ @SuppressLint("VisiblySynchronized") // Legacy MessageQueue synchronizes on this
+ @Nullable
+ Message popForTest() {
+ throwIfNotTest();
+ if (mUseConcurrent) {
+ return nextMessage(false);
+ } else {
+ return legacyPeekOrPop(false);
+ }
+ }
+
+ /**
+ * @return true if we are blocked on a sync barrier
+ */
+ boolean isBlockedOnSyncBarrier() {
+ throwIfNotTest();
+ if (mUseConcurrent) {
+ Iterator<MessageNode> queueIter = mPriorityQueue.iterator();
+ MessageNode queueNode = iterateNext(queueIter);
+
+ if (queueNode.isBarrier()) {
+ long now = SystemClock.uptimeMillis();
+
+ /* Look for a deliverable async node. If one exists we are not blocked. */
+ Iterator<MessageNode> asyncQueueIter = mAsyncPriorityQueue.iterator();
+ MessageNode asyncNode = iterateNext(asyncQueueIter);
+ if (asyncNode != null && now >= asyncNode.getWhen()) {
+ return false;
+ }
+ /*
+ * Look for a deliverable sync node. In this case, if one exists we are blocked
+ * since the barrier prevents delivery of the Message.
+ */
+ while (queueNode.isBarrier()) {
+ queueNode = iterateNext(queueIter);
+ }
+ if (queueNode != null && now >= queueNode.getWhen()) {
+ return true;
+ }
+
+ return false;
+ }
+ } else {
+ Message msg = mMessages;
+ if (msg != null && msg.target == null) {
+ Message iter = msg;
+ /* Look for a deliverable async node */
+ do {
+ iter = iter.next;
+ } while (iter != null && !iter.isAsynchronous());
+
+ long now = SystemClock.uptimeMillis();
+ if (iter != null && now >= iter.when) {
+ return false;
+ }
+ /*
+ * Look for a deliverable sync node. In this case, if one exists we are blocked
+ * since the barrier prevents delivery of the Message.
+ */
+ iter = msg;
+ do {
+ iter = iter.next;
+ } while (iter != null && (iter.target == null || iter.isAsynchronous()));
+
+ if (iter != null && now >= iter.when) {
+ return true;
+ }
+ return false;
+ }
+ }
+ /* No barrier was found. */
+ return false;
+ }
+
private static final class MatchHandlerWhatAndObject extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
+ final Message m = n.mMessage;
if (m.target == h && m.what == what && (object == null || m.obj == object)) {
return true;
}
@@ -1270,8 +1451,9 @@
private static final class MatchHandlerWhatAndObjectEquals extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
+ final Message m = n.mMessage;
if (m.target == h && m.what == what && (object == null || object.equals(m.obj))) {
return true;
}
@@ -1303,8 +1485,9 @@
private static final class MatchHandlerRunnableAndObject extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
+ final Message m = n.mMessage;
if (m.target == h && m.callback == r && (object == null || m.obj == object)) {
return true;
}
@@ -1337,12 +1520,9 @@
private static final class MatchHandler extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
- if (m.target == h) {
- return true;
- }
- return false;
+ return n.mMessage.target == h;
}
}
private final MatchHandler mMatchHandler = new MatchHandler();
@@ -1520,8 +1700,9 @@
private static final class MatchHandlerRunnableAndObjectEquals extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
+ final Message m = n.mMessage;
if (m.target == h && m.callback == r && (object == null || object.equals(m.obj))) {
return true;
}
@@ -1583,8 +1764,9 @@
private static final class MatchHandlerAndObject extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
+ final Message m = n.mMessage;
if (m.target == h && (object == null || m.obj == object)) {
return true;
}
@@ -1644,8 +1826,9 @@
private static final class MatchHandlerAndObjectEquals extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
+ final Message m = n.mMessage;
if (m.target == h && (object == null || object.equals(m.obj))) {
return true;
}
@@ -1751,7 +1934,7 @@
private static final class MatchAllMessages extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
return true;
}
@@ -1763,9 +1946,10 @@
private static final class MatchAllFutureMessages extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
- if (m.when > when) {
+ final Message m = n.mMessage;
+ if (m.when > when) {
return true;
}
return false;
@@ -2471,7 +2655,7 @@
* This class is used to find matches for hasMessages() and removeMessages()
*/
private abstract static class MessageCompare {
- public abstract boolean compareMessage(Message m, Handler h, int what, Object object,
+ public abstract boolean compareMessage(MessageNode n, Handler h, int what, Object object,
Runnable r, long when);
}
@@ -2506,7 +2690,7 @@
MessageNode p = (MessageNode) top;
while (true) {
- if (compare.compareMessage(p.mMessage, h, what, object, r, when)) {
+ if (compare.compareMessage(p, h, what, object, r, when)) {
found = true;
if (DEBUG) {
Log.d(TAG_C, "stackHasMessages node matches");
@@ -2551,7 +2735,7 @@
while (iterator.hasNext()) {
MessageNode msg = iterator.next();
- if (compare.compareMessage(msg.mMessage, h, what, object, r, when)) {
+ if (compare.compareMessage(msg, h, what, object, r, when)) {
if (removeMatches) {
found = true;
if (queue.remove(msg)) {
diff --git a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
index c2a47d7..576c4cc 100644
--- a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
+++ b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
@@ -16,9 +16,12 @@
package android.os;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.TestApi;
+import android.app.ActivityThread;
+import android.app.Instrumentation;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.ravenwood.annotation.RavenwoodRedirect;
import android.ravenwood.annotation.RavenwoodRedirectionClass;
@@ -364,6 +367,28 @@
mPtr = nativeInit();
}
+ @android.ravenwood.annotation.RavenwoodReplace
+ private static void throwIfNotTest() {
+ final ActivityThread activityThread = ActivityThread.currentActivityThread();
+ if (activityThread == null) {
+ // Only tests can reach here.
+ return;
+ }
+ final Instrumentation instrumentation = activityThread.getInstrumentation();
+ if (instrumentation == null) {
+ // Only tests can reach here.
+ return;
+ }
+ if (instrumentation.isInstrumenting()) {
+ return;
+ }
+ throw new IllegalStateException("Test-only API called not from a test!");
+ }
+
+ private static void throwIfNotTest$ravenwood() {
+ return;
+ }
+
@Override
protected void finalize() throws Throwable {
try {
@@ -384,8 +409,9 @@
private static final class MatchDeliverableMessages extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
- long when) {
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
+ final Message m = n.mMessage;
if (m.when <= when) {
return true;
}
@@ -562,7 +588,7 @@
private static final AtomicLong mMessagesDelivered = new AtomicLong();
private boolean mMessageDirectlyQueued;
- private Message nextMessage() {
+ private Message nextMessage(boolean peek) {
int i = 0;
while (true) {
@@ -724,7 +750,7 @@
if (sState.compareAndSet(this, sStackStateActive, nextOp)) {
mMessageCounts.clearCounts();
if (found != null) {
- if (!removeFromPriorityQueue(found)) {
+ if (!peek && !removeFromPriorityQueue(found)) {
/*
* RemoveMessages() might be able to pull messages out from under us
* However we can detect that here and just loop around if it happens.
@@ -993,8 +1019,9 @@
}
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
- long when) {
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
+ final Message m = n.mMessage;
if (m.target == null && m.arg1 == mBarrierToken) {
return true;
}
@@ -1039,6 +1066,79 @@
}
}
+ private static final class MatchEarliestMessage extends MessageCompare {
+ MessageNode mEarliest = null;
+
+ @Override
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
+ final Message m = n.mMessage;
+ if (mEarliest == null || mEarliest.mMessage.when > m.when) {
+ mEarliest = n;
+ }
+
+ return false;
+ }
+ }
+
+ /**
+ * Get the timestamp of the next executable message in our priority queue.
+ * Returns null if there are no messages ready for delivery.
+ *
+ * Caller must ensure that this doesn't race 'next' from the Looper thread.
+ */
+ @SuppressLint("VisiblySynchronized") // Legacy MessageQueue synchronizes on this
+ Long peekWhenForTest() {
+ throwIfNotTest();
+ Message ret = nextMessage(true);
+ return ret != null ? ret.when : null;
+ }
+
+ /**
+ * Return the next executable message in our priority queue.
+ * Returns null if there are no messages ready for delivery
+ *
+ * Caller must ensure that this doesn't race 'next' from the Looper thread.
+ */
+ @SuppressLint("VisiblySynchronized") // Legacy MessageQueue synchronizes on this
+ @Nullable
+ Message popForTest() {
+ throwIfNotTest();
+ return nextMessage(false);
+ }
+
+ /**
+ * @return true if we are blocked on a sync barrier
+ */
+ boolean isBlockedOnSyncBarrier() {
+ throwIfNotTest();
+ Iterator<MessageNode> queueIter = mPriorityQueue.iterator();
+ MessageNode queueNode = iterateNext(queueIter);
+
+ if (queueNode.isBarrier()) {
+ long now = SystemClock.uptimeMillis();
+
+ /* Look for a deliverable async node. If one exists we are not blocked. */
+ Iterator<MessageNode> asyncQueueIter = mAsyncPriorityQueue.iterator();
+ MessageNode asyncNode = iterateNext(asyncQueueIter);
+ if (asyncNode != null && now >= asyncNode.getWhen()) {
+ return false;
+ }
+ /*
+ * Look for a deliverable sync node. In this case, if one exists we are blocked
+ * since the barrier prevents delivery of the Message.
+ */
+ while (queueNode.isBarrier()) {
+ queueNode = iterateNext(queueIter);
+ }
+ if (queueNode != null && now >= queueNode.getWhen()) {
+ return true;
+ }
+
+ return false;
+ }
+ }
+
private StateNode getStateNode(StackNode node) {
if (node.isMessageNode()) {
return ((MessageNode) node).mBottomOfStack;
@@ -1058,7 +1158,7 @@
* This class is used to find matches for hasMessages() and removeMessages()
*/
private abstract static class MessageCompare {
- public abstract boolean compareMessage(Message m, Handler h, int what, Object object,
+ public abstract boolean compareMessage(MessageNode n, Handler h, int what, Object object,
Runnable r, long when);
}
@@ -1167,8 +1267,9 @@
private static final class MatchHandlerWhatAndObject extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
- long when) {
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
+ final Message m = n.mMessage;
if (m.target == h && m.what == what && (object == null || m.obj == object)) {
return true;
}
@@ -1187,8 +1288,9 @@
private static final class MatchHandlerWhatAndObjectEquals extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
long when) {
+ final Message m = n.mMessage;
if (m.target == h && m.what == what && (object == null || object.equals(m.obj))) {
return true;
}
@@ -1208,8 +1310,9 @@
private static final class MatchHandlerRunnableAndObject extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
- long when) {
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
+ final Message m = n.mMessage;
if (m.target == h && m.callback == r && (object == null || m.obj == object)) {
return true;
}
@@ -1229,8 +1332,9 @@
private static final class MatchHandler extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
- long when) {
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
+ final Message m = n.mMessage;
if (m.target == h) {
return true;
}
@@ -1268,8 +1372,9 @@
private static final class MatchHandlerRunnableAndObjectEquals extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
- long when) {
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
+ final Message m = n.mMessage;
if (m.target == h && m.callback == r && (object == null || object.equals(m.obj))) {
return true;
}
@@ -1287,8 +1392,9 @@
private static final class MatchHandlerAndObject extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
- long when) {
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
+ final Message m = n.mMessage;
if (m.target == h && (object == null || m.obj == object)) {
return true;
}
@@ -1305,8 +1411,9 @@
private static final class MatchHandlerAndObjectEquals extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
- long when) {
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
+ final Message m = n.mMessage;
if (m.target == h && (object == null || object.equals(m.obj))) {
return true;
}
@@ -1324,8 +1431,8 @@
private static final class MatchAllMessages extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
- long when) {
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
return true;
}
}
@@ -1336,8 +1443,9 @@
private static final class MatchAllFutureMessages extends MessageCompare {
@Override
- public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
- long when) {
+ public boolean compareMessage(MessageNode n, Handler h, int what, Object object,
+ Runnable r, long when) {
+ final Message m = n.mMessage;
if (m.when > when) {
return true;
}
diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java
index 8db1567..a2e9314 100644
--- a/core/java/android/os/IpcDataCache.java
+++ b/core/java/android/os/IpcDataCache.java
@@ -400,10 +400,11 @@
}
/**
- * This is a convenience class that encapsulates configuration information for a
- * cache. It may be supplied to the cache constructors in lieu of the other
- * parameters. The class captures maximum entry count, the module, the key, and the
- * api.
+ * This is a convenience class that encapsulates configuration information for a cache. It
+ * may be supplied to the cache constructors in lieu of the other parameters. The class
+ * captures maximum entry count, the module, the key, and the api. The key is used to
+ * invalidate the cache and may be shared by different caches. The api is a user-visible (in
+ * debug) name for the cache.
*
* There are three specific use cases supported by this class.
*
@@ -430,11 +431,8 @@
* @hide
*/
public static class Config {
- private final int mMaxEntries;
- @IpcDataCacheModule
- private final String mModule;
- private final String mApi;
- private final String mName;
+ final Args mArgs;
+ final String mName;
/**
* The list of cache names that were created extending this Config. If
@@ -452,12 +450,20 @@
*/
private boolean mDisabled = false;
+ /**
+ * Fully construct a config.
+ */
+ private Config(@NonNull Args args, @NonNull String name) {
+ mArgs = args;
+ mName = name;
+ }
+
+ /**
+ *
+ */
public Config(int maxEntries, @NonNull @IpcDataCacheModule String module,
@NonNull String api, @NonNull String name) {
- mMaxEntries = maxEntries;
- mModule = module;
- mApi = api;
- mName = name;
+ this(new Args(module).api(api).maxEntries(maxEntries), name);
}
/**
@@ -473,7 +479,7 @@
* the parameter list.
*/
public Config(@NonNull Config root, @NonNull String api, @NonNull String name) {
- this(root.maxEntries(), root.module(), api, name);
+ this(root.mArgs.api(api), name);
}
/**
@@ -481,7 +487,7 @@
* the parameter list.
*/
public Config(@NonNull Config root, @NonNull String api) {
- this(root.maxEntries(), root.module(), api, api);
+ this(root.mArgs.api(api), api);
}
/**
@@ -490,26 +496,23 @@
* current process.
*/
public Config child(@NonNull String name) {
- final Config result = new Config(this, api(), name);
+ final Config result = new Config(mArgs, name);
registerChild(name);
return result;
}
- public final int maxEntries() {
- return mMaxEntries;
+ /**
+ * Set the cacheNull behavior.
+ */
+ public Config cacheNulls(boolean enable) {
+ return new Config(mArgs.cacheNulls(enable), mName);
}
- @IpcDataCacheModule
- public final @NonNull String module() {
- return mModule;
- }
-
- public final @NonNull String api() {
- return mApi;
- }
-
- public final @NonNull String name() {
- return mName;
+ /**
+ * Set the isolateUidss behavior.
+ */
+ public Config isolateUids(boolean enable) {
+ return new Config(mArgs.isolateUids(enable), mName);
}
/**
@@ -532,7 +535,7 @@
* Invalidate all caches that share this Config's module and api.
*/
public void invalidateCache() {
- IpcDataCache.invalidateCache(mModule, mApi);
+ IpcDataCache.invalidateCache(mArgs);
}
/**
@@ -564,8 +567,7 @@
* @hide
*/
public IpcDataCache(@NonNull Config config, @NonNull QueryHandler<Query, Result> computer) {
- super(new Args(config.module()).maxEntries(config.maxEntries()).api(config.api()),
- config.name(), computer);
+ super(config.mArgs, config.mName, computer);
}
/**
diff --git a/core/java/android/os/LegacyMessageQueue/MessageQueue.java b/core/java/android/os/LegacyMessageQueue/MessageQueue.java
index cae82d0..10d0904 100644
--- a/core/java/android/os/LegacyMessageQueue/MessageQueue.java
+++ b/core/java/android/os/LegacyMessageQueue/MessageQueue.java
@@ -16,9 +16,14 @@
package android.os;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
+import android.app.ActivityThread;
+import android.app.Instrumentation;
import android.compat.annotation.UnsupportedAppUsage;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.ravenwood.annotation.RavenwoodRedirect;
@@ -99,6 +104,28 @@
mPtr = nativeInit();
}
+ @android.ravenwood.annotation.RavenwoodReplace
+ private static void throwIfNotTest() {
+ final ActivityThread activityThread = ActivityThread.currentActivityThread();
+ if (activityThread == null) {
+ // Only tests can reach here.
+ return;
+ }
+ final Instrumentation instrumentation = activityThread.getInstrumentation();
+ if (instrumentation == null) {
+ // Only tests can reach here.
+ return;
+ }
+ if (instrumentation.isInstrumenting()) {
+ return;
+ }
+ throw new IllegalStateException("Test-only API called not from a test!");
+ }
+
+ private static void throwIfNotTest$ravenwood() {
+ return;
+ }
+
@Override
protected void finalize() throws Throwable {
try {
@@ -713,6 +740,112 @@
return true;
}
+ private Message legacyPeekOrPop(boolean peek) {
+ synchronized (this) {
+ // Try to retrieve the next message. Return if found.
+ final long now = SystemClock.uptimeMillis();
+ Message prevMsg = null;
+ Message msg = mMessages;
+ if (msg != null && msg.target == null) {
+ // Stalled by a barrier. Find the next asynchronous message in the queue.
+ do {
+ prevMsg = msg;
+ msg = msg.next;
+ } while (msg != null && !msg.isAsynchronous());
+ }
+ if (msg != null) {
+ if (now >= msg.when) {
+ // Got a message.
+ mBlocked = false;
+ if (peek) {
+ return msg;
+ }
+ if (prevMsg != null) {
+ prevMsg.next = msg.next;
+ if (prevMsg.next == null) {
+ mLast = prevMsg;
+ }
+ } else {
+ mMessages = msg.next;
+ if (msg.next == null) {
+ mLast = null;
+ }
+ }
+ msg.next = null;
+ msg.markInUse();
+ if (msg.isAsynchronous()) {
+ mAsyncMessageCount--;
+ }
+ if (TRACE) {
+ Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet());
+ }
+ return msg;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the timestamp of the next executable message in our priority queue.
+ * Returns null if there are no messages ready for delivery.
+ *
+ * Caller must ensure that this doesn't race 'next' from the Looper thread.
+ */
+ @SuppressLint("VisiblySynchronized") // Legacy MessageQueue synchronizes on this
+ Long peekWhenForTest() {
+ throwIfNotTest();
+ Message ret = legacyPeekOrPop(true);
+ return ret != null ? ret.when : null;
+ }
+
+ /**
+ * Return the next executable message in our priority queue.
+ * Returns null if there are no messages ready for delivery
+ *
+ * Caller must ensure that this doesn't race 'next' from the Looper thread.
+ */
+ @SuppressLint("VisiblySynchronized") // Legacy MessageQueue synchronizes on this
+ @Nullable
+ Message popForTest() {
+ throwIfNotTest();
+ return legacyPeekOrPop(false);
+ }
+
+ /**
+ * @return true if we are blocked on a sync barrier
+ */
+ boolean isBlockedOnSyncBarrier() {
+ throwIfNotTest();
+ Message msg = mMessages;
+ if (msg != null && msg.target == null) {
+ Message iter = msg;
+ /* Look for a deliverable async node */
+ do {
+ iter = iter.next;
+ } while (iter != null && !iter.isAsynchronous());
+
+ long now = SystemClock.uptimeMillis();
+ if (iter != null && now >= iter.when) {
+ return false;
+ }
+ /*
+ * Look for a deliverable sync node. In this case, if one exists we are blocked
+ * since the barrier prevents delivery of the Message.
+ */
+ iter = msg;
+ do {
+ iter = iter.next;
+ } while (iter != null && (iter.target == null || iter.isAsynchronous()));
+
+ if (iter != null && now >= iter.when) {
+ return true;
+ }
+ return false;
+ }
+ return false;
+ }
+
boolean hasMessages(Handler h, int what, Object object) {
if (h == null) {
return false;
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index bfcc5cc..8d35338 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -118,9 +118,10 @@
# Memory
per-file OomKillRecord.java = file:/MEMORY_OWNERS
-# MessageQueue
+# MessageQueue and related classes
per-file MessageQueue.java = mfasheh@google.com, shayba@google.com
per-file Message.java = mfasheh@google.com, shayba@google.com
+per-file TestLooperManager.java = mfasheh@google.com, shayba@google.com
# Stats
per-file IStatsBootstrapAtomService.aidl = file:/services/core/java/com/android/server/stats/OWNERS
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 6855d95..cf473ec 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -50,7 +50,6 @@
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
-import dalvik.annotation.optimization.NeverInline;
import libcore.util.SneakyThrow;
@@ -588,17 +587,6 @@
return parcel;
}
- @NeverInline
- private void errorUsedWhileRecycling() {
- Log.wtf(TAG, "Parcel used while recycled. "
- + Log.getStackTraceString(new Throwable())
- + " Original recycle call (if DEBUG_RECYCLE): ", mStack);
- }
-
- private void assertNotRecycled() {
- if (mRecycled) errorUsedWhileRecycling();
- }
-
/**
* Put a Parcel object back into the pool. You must not touch
* the object after this call.
@@ -647,7 +635,6 @@
* @hide
*/
public void setReadWriteHelper(@Nullable ReadWriteHelper helper) {
- assertNotRecycled();
mReadWriteHelper = helper != null ? helper : ReadWriteHelper.DEFAULT;
}
@@ -657,7 +644,6 @@
* @hide
*/
public boolean hasReadWriteHelper() {
- assertNotRecycled();
return (mReadWriteHelper != null) && (mReadWriteHelper != ReadWriteHelper.DEFAULT);
}
@@ -684,7 +670,6 @@
* @hide
*/
public final void markSensitive() {
- assertNotRecycled();
nativeMarkSensitive(mNativePtr);
}
@@ -701,7 +686,6 @@
* @hide
*/
public final boolean isForRpc() {
- assertNotRecycled();
return nativeIsForRpc(mNativePtr);
}
@@ -709,25 +693,21 @@
@ParcelFlags
@TestApi
public int getFlags() {
- assertNotRecycled();
return mFlags;
}
/** @hide */
public void setFlags(@ParcelFlags int flags) {
- assertNotRecycled();
mFlags = flags;
}
/** @hide */
public void addFlags(@ParcelFlags int flags) {
- assertNotRecycled();
mFlags |= flags;
}
/** @hide */
private boolean hasFlags(@ParcelFlags int flags) {
- assertNotRecycled();
return (mFlags & flags) == flags;
}
@@ -740,7 +720,6 @@
// We don't really need to protect it; even if 3p / non-system apps, nothing would happen.
// This would only work when used on a reply parcel by a binder object that's allowed-blocking.
public void setPropagateAllowBlocking() {
- assertNotRecycled();
addFlags(FLAG_PROPAGATE_ALLOW_BLOCKING);
}
@@ -748,7 +727,6 @@
* Returns the total amount of data contained in the parcel.
*/
public int dataSize() {
- assertNotRecycled();
return nativeDataSize(mNativePtr);
}
@@ -757,7 +735,6 @@
* parcel. That is, {@link #dataSize}-{@link #dataPosition}.
*/
public final int dataAvail() {
- assertNotRecycled();
return nativeDataAvail(mNativePtr);
}
@@ -766,7 +743,6 @@
* more than {@link #dataSize}.
*/
public final int dataPosition() {
- assertNotRecycled();
return nativeDataPosition(mNativePtr);
}
@@ -777,7 +753,6 @@
* data buffer.
*/
public final int dataCapacity() {
- assertNotRecycled();
return nativeDataCapacity(mNativePtr);
}
@@ -789,7 +764,6 @@
* @param size The new number of bytes in the Parcel.
*/
public final void setDataSize(int size) {
- assertNotRecycled();
nativeSetDataSize(mNativePtr, size);
}
@@ -799,7 +773,6 @@
* {@link #dataSize}.
*/
public final void setDataPosition(int pos) {
- assertNotRecycled();
nativeSetDataPosition(mNativePtr, pos);
}
@@ -811,13 +784,11 @@
* with this method.
*/
public final void setDataCapacity(int size) {
- assertNotRecycled();
nativeSetDataCapacity(mNativePtr, size);
}
/** @hide */
public final boolean pushAllowFds(boolean allowFds) {
- assertNotRecycled();
return nativePushAllowFds(mNativePtr, allowFds);
}
@@ -838,7 +809,6 @@
* in different versions of the platform.
*/
public final byte[] marshall() {
- assertNotRecycled();
return nativeMarshall(mNativePtr);
}
@@ -846,18 +816,15 @@
* Fills the raw bytes of this Parcel with the supplied data.
*/
public final void unmarshall(@NonNull byte[] data, int offset, int length) {
- assertNotRecycled();
nativeUnmarshall(mNativePtr, data, offset, length);
}
public final void appendFrom(Parcel parcel, int offset, int length) {
- assertNotRecycled();
nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length);
}
/** @hide */
public int compareData(Parcel other) {
- assertNotRecycled();
return nativeCompareData(mNativePtr, other.mNativePtr);
}
@@ -868,7 +835,6 @@
/** @hide */
public final void setClassCookie(Class clz, Object cookie) {
- assertNotRecycled();
if (mClassCookies == null) {
mClassCookies = new ArrayMap<>();
}
@@ -878,13 +844,11 @@
/** @hide */
@Nullable
public final Object getClassCookie(Class clz) {
- assertNotRecycled();
return mClassCookies != null ? mClassCookies.get(clz) : null;
}
/** @hide */
public void removeClassCookie(Class clz, Object expectedCookie) {
- assertNotRecycled();
if (mClassCookies != null) {
Object removedCookie = mClassCookies.remove(clz);
if (removedCookie != expectedCookie) {
@@ -902,25 +866,21 @@
* @hide
*/
public boolean hasClassCookie(Class clz) {
- assertNotRecycled();
return mClassCookies != null && mClassCookies.containsKey(clz);
}
/** @hide */
public final void adoptClassCookies(Parcel from) {
- assertNotRecycled();
mClassCookies = from.mClassCookies;
}
/** @hide */
public Map<Class, Object> copyClassCookies() {
- assertNotRecycled();
return new ArrayMap<>(mClassCookies);
}
/** @hide */
public void putClassCookies(Map<Class, Object> cookies) {
- assertNotRecycled();
if (cookies == null) {
return;
}
@@ -932,9 +892,14 @@
/**
* Report whether the parcel contains any marshalled file descriptors.
+ *
+ * WARNING: Parcelable definitions change over time. Unless you define
+ * a Parcelable yourself OR the Parcelable explicitly guarantees that
+ * it would never include such objects, you should not expect the return
+ * value to stay the same, and your code should continue to work even
+ * if the return value changes.
*/
public boolean hasFileDescriptors() {
- assertNotRecycled();
return nativeHasFileDescriptors(mNativePtr);
}
@@ -942,6 +907,12 @@
* Report whether the parcel contains any marshalled file descriptors in the range defined by
* {@code offset} and {@code length}.
*
+ * WARNING: Parcelable definitions change over time. Unless you define
+ * a Parcelable yourself OR the Parcelable explicitly guarantees that
+ * it would never include such objects, you should not expect the return
+ * value to stay the same, and your code should continue to work even
+ * if the return value changes.
+ *
* @param offset The offset from which the range starts. Should be between 0 and
* {@link #dataSize()}.
* @param length The length of the range. Should be between 0 and {@link #dataSize()} - {@code
@@ -950,7 +921,6 @@
* @throws IllegalArgumentException if the parameters are out of the permitted ranges.
*/
public boolean hasFileDescriptors(int offset, int length) {
- assertNotRecycled();
return nativeHasFileDescriptorsInRange(mNativePtr, offset, length);
}
@@ -963,6 +933,12 @@
* <p>For most cases, it will use the self-reported {@link Parcelable#describeContents()} method
* for that.
*
+ * WARNING: Parcelable definitions change over time. Unless you define
+ * a Parcelable yourself OR the Parcelable explicitly guarantees that
+ * it would never include such objects, you should not expect the return
+ * value to stay the same, and your code should continue to work even
+ * if the return value changes.
+ *
* @throws IllegalArgumentException if you provide any object not supported by above methods
* (including if the unsupported object is inside a nested container).
*
@@ -1032,10 +1008,16 @@
*
* @throws UnsupportedOperationException if binder kernel driver was disabled or if method was
* invoked in case of Binder RPC protocol.
+ *
+ * WARNING: Parcelable definitions change over time. Unless you define
+ * a Parcelable yourself OR the Parcelable explicitly guarantees that
+ * it would never include such objects, you should not expect the return
+ * value to stay the same, and your code should continue to work even
+ * if the return value changes.
+ *
* @hide
*/
public boolean hasBinders() {
- assertNotRecycled();
return nativeHasBinders(mNativePtr);
}
@@ -1043,6 +1025,12 @@
* Report whether the parcel contains any marshalled {@link IBinder} objects in the range
* defined by {@code offset} and {@code length}.
*
+ * WARNING: Parcelable definitions change over time. Unless you define
+ * a Parcelable yourself OR the Parcelable explicitly guarantees that
+ * it would never include such objects, you should not expect the return
+ * value to stay the same, and your code should continue to work even
+ * if the return value changes.
+ *
* @param offset The offset from which the range starts. Should be between 0 and
* {@link #dataSize()}.
* @param length The length of the range. Should be between 0 and {@link #dataSize()} - {@code
@@ -1053,7 +1041,6 @@
* @hide
*/
public boolean hasBinders(int offset, int length) {
- assertNotRecycled();
return nativeHasBindersInRange(mNativePtr, offset, length);
}
@@ -1064,7 +1051,6 @@
* at the beginning of transactions as a header.
*/
public final void writeInterfaceToken(@NonNull String interfaceName) {
- assertNotRecycled();
nativeWriteInterfaceToken(mNativePtr, interfaceName);
}
@@ -1075,7 +1061,6 @@
* should propagate to the caller.
*/
public final void enforceInterface(@NonNull String interfaceName) {
- assertNotRecycled();
nativeEnforceInterface(mNativePtr, interfaceName);
}
@@ -1086,7 +1071,6 @@
* When used over binder, this exception should propagate to the caller.
*/
public void enforceNoDataAvail() {
- assertNotRecycled();
final int n = dataAvail();
if (n > 0) {
throw new BadParcelableException("Parcel data not fully consumed, unread size: " + n);
@@ -1103,7 +1087,6 @@
* @hide
*/
public boolean replaceCallingWorkSourceUid(int workSourceUid) {
- assertNotRecycled();
return nativeReplaceCallingWorkSourceUid(mNativePtr, workSourceUid);
}
@@ -1120,7 +1103,6 @@
* @hide
*/
public int readCallingWorkSourceUid() {
- assertNotRecycled();
return nativeReadCallingWorkSourceUid(mNativePtr);
}
@@ -1130,7 +1112,6 @@
* @param b Bytes to place into the parcel.
*/
public final void writeByteArray(@Nullable byte[] b) {
- assertNotRecycled();
writeByteArray(b, 0, (b != null) ? b.length : 0);
}
@@ -1142,7 +1123,6 @@
* @param len Number of bytes to write.
*/
public final void writeByteArray(@Nullable byte[] b, int offset, int len) {
- assertNotRecycled();
if (b == null) {
writeInt(-1);
return;
@@ -1164,7 +1144,6 @@
* @see #readBlob()
*/
public final void writeBlob(@Nullable byte[] b) {
- assertNotRecycled();
writeBlob(b, 0, (b != null) ? b.length : 0);
}
@@ -1183,7 +1162,6 @@
* @see #readBlob()
*/
public final void writeBlob(@Nullable byte[] b, int offset, int len) {
- assertNotRecycled();
if (b == null) {
writeInt(-1);
return;
@@ -1202,7 +1180,6 @@
* growing dataCapacity() if needed.
*/
public final void writeInt(int val) {
- assertNotRecycled();
int err = nativeWriteInt(mNativePtr, val);
if (err != OK) {
nativeSignalExceptionForError(err);
@@ -1214,7 +1191,6 @@
* growing dataCapacity() if needed.
*/
public final void writeLong(long val) {
- assertNotRecycled();
int err = nativeWriteLong(mNativePtr, val);
if (err != OK) {
nativeSignalExceptionForError(err);
@@ -1226,7 +1202,6 @@
* dataPosition(), growing dataCapacity() if needed.
*/
public final void writeFloat(float val) {
- assertNotRecycled();
int err = nativeWriteFloat(mNativePtr, val);
if (err != OK) {
nativeSignalExceptionForError(err);
@@ -1238,7 +1213,6 @@
* current dataPosition(), growing dataCapacity() if needed.
*/
public final void writeDouble(double val) {
- assertNotRecycled();
int err = nativeWriteDouble(mNativePtr, val);
if (err != OK) {
nativeSignalExceptionForError(err);
@@ -1250,19 +1224,16 @@
* growing dataCapacity() if needed.
*/
public final void writeString(@Nullable String val) {
- assertNotRecycled();
writeString16(val);
}
/** {@hide} */
public final void writeString8(@Nullable String val) {
- assertNotRecycled();
mReadWriteHelper.writeString8(this, val);
}
/** {@hide} */
public final void writeString16(@Nullable String val) {
- assertNotRecycled();
mReadWriteHelper.writeString16(this, val);
}
@@ -1274,19 +1245,16 @@
* @hide
*/
public void writeStringNoHelper(@Nullable String val) {
- assertNotRecycled();
writeString16NoHelper(val);
}
/** {@hide} */
public void writeString8NoHelper(@Nullable String val) {
- assertNotRecycled();
nativeWriteString8(mNativePtr, val);
}
/** {@hide} */
public void writeString16NoHelper(@Nullable String val) {
- assertNotRecycled();
nativeWriteString16(mNativePtr, val);
}
@@ -1298,7 +1266,6 @@
* for true or false, respectively, but may change in the future.
*/
public final void writeBoolean(boolean val) {
- assertNotRecycled();
writeInt(val ? 1 : 0);
}
@@ -1310,7 +1277,6 @@
@UnsupportedAppUsage
@RavenwoodThrow(blockedBy = android.text.Spanned.class)
public final void writeCharSequence(@Nullable CharSequence val) {
- assertNotRecycled();
TextUtils.writeToParcel(val, this, 0);
}
@@ -1319,7 +1285,6 @@
* growing dataCapacity() if needed.
*/
public final void writeStrongBinder(IBinder val) {
- assertNotRecycled();
nativeWriteStrongBinder(mNativePtr, val);
}
@@ -1328,7 +1293,6 @@
* growing dataCapacity() if needed.
*/
public final void writeStrongInterface(IInterface val) {
- assertNotRecycled();
writeStrongBinder(val == null ? null : val.asBinder());
}
@@ -1343,7 +1307,6 @@
* if {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set.</p>
*/
public final void writeFileDescriptor(@NonNull FileDescriptor val) {
- assertNotRecycled();
nativeWriteFileDescriptor(mNativePtr, val);
}
@@ -1352,7 +1315,6 @@
* This will be the new name for writeFileDescriptor, for consistency.
**/
public final void writeRawFileDescriptor(@NonNull FileDescriptor val) {
- assertNotRecycled();
nativeWriteFileDescriptor(mNativePtr, val);
}
@@ -1363,7 +1325,6 @@
* @param value The array of objects to be written.
*/
public final void writeRawFileDescriptorArray(@Nullable FileDescriptor[] value) {
- assertNotRecycled();
if (value != null) {
int N = value.length;
writeInt(N);
@@ -1383,7 +1344,6 @@
* the future.
*/
public final void writeByte(byte val) {
- assertNotRecycled();
writeInt(val);
}
@@ -1399,7 +1359,6 @@
* allows you to avoid mysterious type errors at the point of marshalling.
*/
public final void writeMap(@Nullable Map val) {
- assertNotRecycled();
writeMapInternal((Map<String, Object>) val);
}
@@ -1408,7 +1367,6 @@
* growing dataCapacity() if needed. The Map keys must be String objects.
*/
/* package */ void writeMapInternal(@Nullable Map<String,Object> val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1434,7 +1392,6 @@
* growing dataCapacity() if needed. The Map keys must be String objects.
*/
/* package */ void writeArrayMapInternal(@Nullable ArrayMap<String, Object> val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1464,7 +1421,6 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void writeArrayMap(@Nullable ArrayMap<String, Object> val) {
- assertNotRecycled();
writeArrayMapInternal(val);
}
@@ -1483,7 +1439,6 @@
*/
public <T extends Parcelable> void writeTypedArrayMap(@Nullable ArrayMap<String, T> val,
int parcelableFlags) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1505,7 +1460,6 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void writeArraySet(@Nullable ArraySet<? extends Object> val) {
- assertNotRecycled();
final int size = (val != null) ? val.size() : -1;
writeInt(size);
for (int i = 0; i < size; i++) {
@@ -1518,7 +1472,6 @@
* growing dataCapacity() if needed.
*/
public final void writeBundle(@Nullable Bundle val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1532,7 +1485,6 @@
* growing dataCapacity() if needed.
*/
public final void writePersistableBundle(@Nullable PersistableBundle val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1546,7 +1498,6 @@
* growing dataCapacity() if needed.
*/
public final void writeSize(@NonNull Size val) {
- assertNotRecycled();
writeInt(val.getWidth());
writeInt(val.getHeight());
}
@@ -1556,7 +1507,6 @@
* growing dataCapacity() if needed.
*/
public final void writeSizeF(@NonNull SizeF val) {
- assertNotRecycled();
writeFloat(val.getWidth());
writeFloat(val.getHeight());
}
@@ -1567,7 +1517,6 @@
* {@link #writeValue} and must follow the specification there.
*/
public final void writeList(@Nullable List val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1587,7 +1536,6 @@
* {@link #writeValue} and must follow the specification there.
*/
public final void writeArray(@Nullable Object[] val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1608,7 +1556,6 @@
* specification there.
*/
public final <T> void writeSparseArray(@Nullable SparseArray<T> val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1624,7 +1571,6 @@
}
public final void writeSparseBooleanArray(@Nullable SparseBooleanArray val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1643,7 +1589,6 @@
* @hide
*/
public final void writeSparseIntArray(@Nullable SparseIntArray val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -1659,7 +1604,6 @@
}
public final void writeBooleanArray(@Nullable boolean[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -1694,7 +1638,6 @@
}
private void ensureWithinMemoryLimit(int typeSize, @NonNull int... dimensions) {
- assertNotRecycled();
// For Multidimensional arrays, Calculate total object
// which will be allocated.
int totalObjects = 1;
@@ -1712,7 +1655,6 @@
}
private void ensureWithinMemoryLimit(int typeSize, int length) {
- assertNotRecycled();
int estimatedAllocationSize = 0;
try {
estimatedAllocationSize = Math.multiplyExact(typeSize, length);
@@ -1736,7 +1678,6 @@
@Nullable
public final boolean[] createBooleanArray() {
- assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_BOOLEAN, N);
// >>2 as a fast divide-by-4 works in the create*Array() functions
@@ -1754,7 +1695,6 @@
}
public final void readBooleanArray(@NonNull boolean[] val) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -1767,7 +1707,6 @@
/** @hide */
public void writeShortArray(@Nullable short[] val) {
- assertNotRecycled();
if (val != null) {
int n = val.length;
writeInt(n);
@@ -1782,7 +1721,6 @@
/** @hide */
@Nullable
public short[] createShortArray() {
- assertNotRecycled();
int n = readInt();
ensureWithinMemoryLimit(SIZE_SHORT, n);
if (n >= 0 && n <= (dataAvail() >> 2)) {
@@ -1798,7 +1736,6 @@
/** @hide */
public void readShortArray(@NonNull short[] val) {
- assertNotRecycled();
int n = readInt();
if (n == val.length) {
for (int i = 0; i < n; i++) {
@@ -1810,7 +1747,6 @@
}
public final void writeCharArray(@Nullable char[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -1824,7 +1760,6 @@
@Nullable
public final char[] createCharArray() {
- assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_CHAR, N);
if (N >= 0 && N <= (dataAvail() >> 2)) {
@@ -1839,7 +1774,6 @@
}
public final void readCharArray(@NonNull char[] val) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -1851,7 +1785,6 @@
}
public final void writeIntArray(@Nullable int[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -1865,7 +1798,6 @@
@Nullable
public final int[] createIntArray() {
- assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_INT, N);
if (N >= 0 && N <= (dataAvail() >> 2)) {
@@ -1880,7 +1812,6 @@
}
public final void readIntArray(@NonNull int[] val) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -1892,7 +1823,6 @@
}
public final void writeLongArray(@Nullable long[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -1906,7 +1836,6 @@
@Nullable
public final long[] createLongArray() {
- assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_LONG, N);
// >>3 because stored longs are 64 bits
@@ -1922,7 +1851,6 @@
}
public final void readLongArray(@NonNull long[] val) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -1934,7 +1862,6 @@
}
public final void writeFloatArray(@Nullable float[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -1948,7 +1875,6 @@
@Nullable
public final float[] createFloatArray() {
- assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_FLOAT, N);
// >>2 because stored floats are 4 bytes
@@ -1964,7 +1890,6 @@
}
public final void readFloatArray(@NonNull float[] val) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -1976,7 +1901,6 @@
}
public final void writeDoubleArray(@Nullable double[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -1990,7 +1914,6 @@
@Nullable
public final double[] createDoubleArray() {
- assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_DOUBLE, N);
// >>3 because stored doubles are 8 bytes
@@ -2006,7 +1929,6 @@
}
public final void readDoubleArray(@NonNull double[] val) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -2018,24 +1940,20 @@
}
public final void writeStringArray(@Nullable String[] val) {
- assertNotRecycled();
writeString16Array(val);
}
@Nullable
public final String[] createStringArray() {
- assertNotRecycled();
return createString16Array();
}
public final void readStringArray(@NonNull String[] val) {
- assertNotRecycled();
readString16Array(val);
}
/** {@hide} */
public final void writeString8Array(@Nullable String[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -2050,7 +1968,6 @@
/** {@hide} */
@Nullable
public final String[] createString8Array() {
- assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
if (N >= 0) {
@@ -2066,7 +1983,6 @@
/** {@hide} */
public final void readString8Array(@NonNull String[] val) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -2079,7 +1995,6 @@
/** {@hide} */
public final void writeString16Array(@Nullable String[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -2094,7 +2009,6 @@
/** {@hide} */
@Nullable
public final String[] createString16Array() {
- assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
if (N >= 0) {
@@ -2110,7 +2024,6 @@
/** {@hide} */
public final void readString16Array(@NonNull String[] val) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -2122,7 +2035,6 @@
}
public final void writeBinderArray(@Nullable IBinder[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -2147,7 +2059,6 @@
*/
public final <T extends IInterface> void writeInterfaceArray(
@SuppressLint("ArrayReturn") @Nullable T[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -2163,7 +2074,6 @@
* @hide
*/
public final void writeCharSequenceArray(@Nullable CharSequence[] val) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -2179,7 +2089,6 @@
* @hide
*/
public final void writeCharSequenceList(@Nullable ArrayList<CharSequence> val) {
- assertNotRecycled();
if (val != null) {
int N = val.size();
writeInt(N);
@@ -2193,7 +2102,6 @@
@Nullable
public final IBinder[] createBinderArray() {
- assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
if (N >= 0) {
@@ -2208,7 +2116,6 @@
}
public final void readBinderArray(@NonNull IBinder[] val) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -2230,7 +2137,6 @@
@Nullable
public final <T extends IInterface> T[] createInterfaceArray(
@NonNull IntFunction<T[]> newArray, @NonNull Function<IBinder, T> asInterface) {
- assertNotRecycled();
int N = readInt();
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
if (N >= 0) {
@@ -2255,7 +2161,6 @@
public final <T extends IInterface> void readInterfaceArray(
@SuppressLint("ArrayReturn") @NonNull T[] val,
@NonNull Function<IBinder, T> asInterface) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -2281,7 +2186,6 @@
* @see Parcelable
*/
public final <T extends Parcelable> void writeTypedList(@Nullable List<T> val) {
- assertNotRecycled();
writeTypedList(val, 0);
}
@@ -2301,7 +2205,6 @@
*/
public final <T extends Parcelable> void writeTypedSparseArray(@Nullable SparseArray<T> val,
int parcelableFlags) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2331,7 +2234,6 @@
* @see Parcelable
*/
public <T extends Parcelable> void writeTypedList(@Nullable List<T> val, int parcelableFlags) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2357,7 +2259,6 @@
* @see #readStringList
*/
public final void writeStringList(@Nullable List<String> val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2383,7 +2284,6 @@
* @see #readBinderList
*/
public final void writeBinderList(@Nullable List<IBinder> val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2406,7 +2306,6 @@
* @see #readInterfaceList
*/
public final <T extends IInterface> void writeInterfaceList(@Nullable List<T> val) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2428,7 +2327,6 @@
* @see #readParcelableList(List, ClassLoader)
*/
public final <T extends Parcelable> void writeParcelableList(@Nullable List<T> val, int flags) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2463,7 +2361,6 @@
*/
public final <T extends Parcelable> void writeTypedArray(@Nullable T[] val,
int parcelableFlags) {
- assertNotRecycled();
if (val != null) {
int N = val.length;
writeInt(N);
@@ -2486,7 +2383,6 @@
*/
public final <T extends Parcelable> void writeTypedObject(@Nullable T val,
int parcelableFlags) {
- assertNotRecycled();
if (val != null) {
writeInt(1);
val.writeToParcel(this, parcelableFlags);
@@ -2524,7 +2420,6 @@
*/
public <T> void writeFixedArray(@Nullable T val, int parcelableFlags,
@NonNull int... dimensions) {
- assertNotRecycled();
if (val == null) {
writeInt(-1);
return;
@@ -2636,7 +2531,6 @@
* should be used).</p>
*/
public final void writeValue(@Nullable Object v) {
- assertNotRecycled();
if (v instanceof LazyValue) {
LazyValue value = (LazyValue) v;
value.writeToParcel(this);
@@ -2754,7 +2648,6 @@
* @hide
*/
public void writeValue(int type, @Nullable Object v) {
- assertNotRecycled();
switch (type) {
case VAL_NULL:
break;
@@ -2868,7 +2761,6 @@
* {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
*/
public final void writeParcelable(@Nullable Parcelable p, int parcelableFlags) {
- assertNotRecycled();
if (p == null) {
writeString(null);
return;
@@ -2884,7 +2776,6 @@
* @see #readParcelableCreator
*/
public final void writeParcelableCreator(@NonNull Parcelable p) {
- assertNotRecycled();
String name = p.getClass().getName();
writeString(name);
}
@@ -2923,7 +2814,6 @@
*/
@TestApi
public boolean allowSquashing() {
- assertNotRecycled();
boolean previous = mAllowSquashing;
mAllowSquashing = true;
return previous;
@@ -2935,7 +2825,6 @@
*/
@TestApi
public void restoreAllowSquashing(boolean previous) {
- assertNotRecycled();
mAllowSquashing = previous;
if (!mAllowSquashing) {
mWrittenSquashableParcelables = null;
@@ -2992,7 +2881,6 @@
* @hide
*/
public boolean maybeWriteSquashed(@NonNull Parcelable p) {
- assertNotRecycled();
if (!mAllowSquashing) {
// Don't squash, and don't put it in the map either.
writeInt(0);
@@ -3043,7 +2931,6 @@
@SuppressWarnings("unchecked")
@Nullable
public <T extends Parcelable> T readSquashed(SquashReadHelper<T> reader) {
- assertNotRecycled();
final int offset = readInt();
final int pos = dataPosition();
@@ -3077,7 +2964,6 @@
* using the other approaches to writing data in to a Parcel.
*/
public final void writeSerializable(@Nullable Serializable s) {
- assertNotRecycled();
if (s == null) {
writeString(null);
return;
@@ -3130,7 +3016,6 @@
*/
@RavenwoodReplace(blockedBy = AppOpsManager.class)
public final void writeException(@NonNull Exception e) {
- assertNotRecycled();
AppOpsManager.prefixParcelWithAppOpsIfNeeded(this);
int code = getExceptionCode(e);
@@ -3211,7 +3096,6 @@
/** @hide */
public void writeStackTrace(@NonNull Throwable e) {
- assertNotRecycled();
final int sizePosition = dataPosition();
writeInt(0); // Header size will be filled in later
StackTraceElement[] stackTrace = e.getStackTrace();
@@ -3237,7 +3121,6 @@
*/
@RavenwoodReplace(blockedBy = AppOpsManager.class)
public final void writeNoException() {
- assertNotRecycled();
AppOpsManager.prefixParcelWithAppOpsIfNeeded(this);
// Despite the name of this function ("write no exception"),
@@ -3281,7 +3164,6 @@
* @see #writeNoException
*/
public final void readException() {
- assertNotRecycled();
int code = readExceptionCode();
if (code != 0) {
String msg = readString();
@@ -3305,7 +3187,6 @@
@UnsupportedAppUsage
@TestApi
public final int readExceptionCode() {
- assertNotRecycled();
int code = readInt();
if (code == EX_HAS_NOTED_APPOPS_REPLY_HEADER) {
AppOpsManager.readAndLogNotedAppops(this);
@@ -3339,7 +3220,6 @@
* @param msg The exception message.
*/
public final void readException(int code, String msg) {
- assertNotRecycled();
String remoteStackTrace = null;
final int remoteStackPayloadSize = readInt();
if (remoteStackPayloadSize > 0) {
@@ -3370,7 +3250,6 @@
/** @hide */
public Exception createExceptionOrNull(int code, String msg) {
- assertNotRecycled();
switch (code) {
case EX_PARCELABLE:
if (readInt() > 0) {
@@ -3403,7 +3282,6 @@
* Read an integer value from the parcel at the current dataPosition().
*/
public final int readInt() {
- assertNotRecycled();
return nativeReadInt(mNativePtr);
}
@@ -3411,7 +3289,6 @@
* Read a long integer value from the parcel at the current dataPosition().
*/
public final long readLong() {
- assertNotRecycled();
return nativeReadLong(mNativePtr);
}
@@ -3420,7 +3297,6 @@
* dataPosition().
*/
public final float readFloat() {
- assertNotRecycled();
return nativeReadFloat(mNativePtr);
}
@@ -3429,7 +3305,6 @@
* current dataPosition().
*/
public final double readDouble() {
- assertNotRecycled();
return nativeReadDouble(mNativePtr);
}
@@ -3438,19 +3313,16 @@
*/
@Nullable
public final String readString() {
- assertNotRecycled();
return readString16();
}
/** {@hide} */
public final @Nullable String readString8() {
- assertNotRecycled();
return mReadWriteHelper.readString8(this);
}
/** {@hide} */
public final @Nullable String readString16() {
- assertNotRecycled();
return mReadWriteHelper.readString16(this);
}
@@ -3462,19 +3334,16 @@
* @hide
*/
public @Nullable String readStringNoHelper() {
- assertNotRecycled();
return readString16NoHelper();
}
/** {@hide} */
public @Nullable String readString8NoHelper() {
- assertNotRecycled();
return nativeReadString8(mNativePtr);
}
/** {@hide} */
public @Nullable String readString16NoHelper() {
- assertNotRecycled();
return nativeReadString16(mNativePtr);
}
@@ -3482,7 +3351,6 @@
* Read a boolean value from the parcel at the current dataPosition().
*/
public final boolean readBoolean() {
- assertNotRecycled();
return readInt() != 0;
}
@@ -3493,7 +3361,6 @@
@UnsupportedAppUsage
@Nullable
public final CharSequence readCharSequence() {
- assertNotRecycled();
return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(this);
}
@@ -3501,7 +3368,6 @@
* Read an object from the parcel at the current dataPosition().
*/
public final IBinder readStrongBinder() {
- assertNotRecycled();
final IBinder result = nativeReadStrongBinder(mNativePtr);
// If it's a reply from a method with @PropagateAllowBlocking, then inherit allow-blocking
@@ -3517,7 +3383,6 @@
* Read a FileDescriptor from the parcel at the current dataPosition().
*/
public final ParcelFileDescriptor readFileDescriptor() {
- assertNotRecycled();
FileDescriptor fd = nativeReadFileDescriptor(mNativePtr);
return fd != null ? new ParcelFileDescriptor(fd) : null;
}
@@ -3525,7 +3390,6 @@
/** {@hide} */
@UnsupportedAppUsage
public final FileDescriptor readRawFileDescriptor() {
- assertNotRecycled();
return nativeReadFileDescriptor(mNativePtr);
}
@@ -3536,7 +3400,6 @@
**/
@Nullable
public final FileDescriptor[] createRawFileDescriptorArray() {
- assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -3556,7 +3419,6 @@
* @return the FileDescriptor array, or null if the array is null.
**/
public final void readRawFileDescriptorArray(FileDescriptor[] val) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -3571,7 +3433,6 @@
* Read a byte value from the parcel at the current dataPosition().
*/
public final byte readByte() {
- assertNotRecycled();
return (byte)(readInt() & 0xff);
}
@@ -3586,7 +3447,6 @@
*/
@Deprecated
public final void readMap(@NonNull Map outVal, @Nullable ClassLoader loader) {
- assertNotRecycled();
readMapInternal(outVal, loader, /* clazzKey */ null, /* clazzValue */ null);
}
@@ -3600,7 +3460,6 @@
public <K, V> void readMap(@NonNull Map<? super K, ? super V> outVal,
@Nullable ClassLoader loader, @NonNull Class<K> clazzKey,
@NonNull Class<V> clazzValue) {
- assertNotRecycled();
Objects.requireNonNull(clazzKey);
Objects.requireNonNull(clazzValue);
readMapInternal(outVal, loader, clazzKey, clazzValue);
@@ -3619,7 +3478,6 @@
*/
@Deprecated
public final void readList(@NonNull List outVal, @Nullable ClassLoader loader) {
- assertNotRecycled();
int N = readInt();
readListInternal(outVal, N, loader, /* clazz */ null);
}
@@ -3641,7 +3499,6 @@
*/
public <T> void readList(@NonNull List<? super T> outVal,
@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
- assertNotRecycled();
Objects.requireNonNull(clazz);
int n = readInt();
readListInternal(outVal, n, loader, clazz);
@@ -3661,7 +3518,6 @@
@Deprecated
@Nullable
public HashMap readHashMap(@Nullable ClassLoader loader) {
- assertNotRecycled();
return readHashMapInternal(loader, /* clazzKey */ null, /* clazzValue */ null);
}
@@ -3676,7 +3532,6 @@
@Nullable
public <K, V> HashMap<K, V> readHashMap(@Nullable ClassLoader loader,
@NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) {
- assertNotRecycled();
Objects.requireNonNull(clazzKey);
Objects.requireNonNull(clazzValue);
return readHashMapInternal(loader, clazzKey, clazzValue);
@@ -3689,7 +3544,6 @@
*/
@Nullable
public final Bundle readBundle() {
- assertNotRecycled();
return readBundle(null);
}
@@ -3701,7 +3555,6 @@
*/
@Nullable
public final Bundle readBundle(@Nullable ClassLoader loader) {
- assertNotRecycled();
int length = readInt();
if (length < 0) {
if (Bundle.DEBUG) Log.d(TAG, "null bundle: length=" + length);
@@ -3722,7 +3575,6 @@
*/
@Nullable
public final PersistableBundle readPersistableBundle() {
- assertNotRecycled();
return readPersistableBundle(null);
}
@@ -3734,7 +3586,6 @@
*/
@Nullable
public final PersistableBundle readPersistableBundle(@Nullable ClassLoader loader) {
- assertNotRecycled();
int length = readInt();
if (length < 0) {
if (Bundle.DEBUG) Log.d(TAG, "null bundle: length=" + length);
@@ -3753,7 +3604,6 @@
*/
@NonNull
public final Size readSize() {
- assertNotRecycled();
final int width = readInt();
final int height = readInt();
return new Size(width, height);
@@ -3764,7 +3614,6 @@
*/
@NonNull
public final SizeF readSizeF() {
- assertNotRecycled();
final float width = readFloat();
final float height = readFloat();
return new SizeF(width, height);
@@ -3775,7 +3624,6 @@
*/
@Nullable
public final byte[] createByteArray() {
- assertNotRecycled();
return nativeCreateByteArray(mNativePtr);
}
@@ -3784,7 +3632,6 @@
* given byte array.
*/
public final void readByteArray(@NonNull byte[] val) {
- assertNotRecycled();
boolean valid = nativeReadByteArray(mNativePtr, val, (val != null) ? val.length : 0);
if (!valid) {
throw new RuntimeException("bad array lengths");
@@ -3797,7 +3644,6 @@
*/
@Nullable
public final byte[] readBlob() {
- assertNotRecycled();
return nativeReadBlob(mNativePtr);
}
@@ -3808,7 +3654,6 @@
@UnsupportedAppUsage
@Nullable
public final String[] readStringArray() {
- assertNotRecycled();
return createString16Array();
}
@@ -3818,7 +3663,6 @@
*/
@Nullable
public final CharSequence[] readCharSequenceArray() {
- assertNotRecycled();
CharSequence[] array = null;
int length = readInt();
@@ -3841,7 +3685,6 @@
*/
@Nullable
public final ArrayList<CharSequence> readCharSequenceList() {
- assertNotRecycled();
ArrayList<CharSequence> array = null;
int length = readInt();
@@ -3871,7 +3714,6 @@
@Deprecated
@Nullable
public ArrayList readArrayList(@Nullable ClassLoader loader) {
- assertNotRecycled();
return readArrayListInternal(loader, /* clazz */ null);
}
@@ -3894,7 +3736,6 @@
@Nullable
public <T> ArrayList<T> readArrayList(@Nullable ClassLoader loader,
@NonNull Class<? extends T> clazz) {
- assertNotRecycled();
Objects.requireNonNull(clazz);
return readArrayListInternal(loader, clazz);
}
@@ -3914,7 +3755,6 @@
@Deprecated
@Nullable
public Object[] readArray(@Nullable ClassLoader loader) {
- assertNotRecycled();
return readArrayInternal(loader, /* clazz */ null);
}
@@ -3936,7 +3776,6 @@
@SuppressLint({"ArrayReturn", "NullableCollection"})
@Nullable
public <T> T[] readArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
- assertNotRecycled();
Objects.requireNonNull(clazz);
return readArrayInternal(loader, clazz);
}
@@ -3956,7 +3795,6 @@
@Deprecated
@Nullable
public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader) {
- assertNotRecycled();
return readSparseArrayInternal(loader, /* clazz */ null);
}
@@ -3978,7 +3816,6 @@
@Nullable
public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader,
@NonNull Class<? extends T> clazz) {
- assertNotRecycled();
Objects.requireNonNull(clazz);
return readSparseArrayInternal(loader, clazz);
}
@@ -3990,7 +3827,6 @@
*/
@Nullable
public final SparseBooleanArray readSparseBooleanArray() {
- assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -4007,7 +3843,6 @@
*/
@Nullable
public final SparseIntArray readSparseIntArray() {
- assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -4032,7 +3867,6 @@
*/
@Nullable
public final <T> ArrayList<T> createTypedArrayList(@NonNull Parcelable.Creator<T> c) {
- assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -4056,7 +3890,6 @@
* @see #writeTypedList
*/
public final <T> void readTypedList(@NonNull List<T> list, @NonNull Parcelable.Creator<T> c) {
- assertNotRecycled();
int M = list.size();
int N = readInt();
int i = 0;
@@ -4086,7 +3919,6 @@
*/
public final @Nullable <T extends Parcelable> SparseArray<T> createTypedSparseArray(
@NonNull Parcelable.Creator<T> creator) {
- assertNotRecycled();
final int count = readInt();
if (count < 0) {
return null;
@@ -4116,7 +3948,6 @@
*/
public final @Nullable <T extends Parcelable> ArrayMap<String, T> createTypedArrayMap(
@NonNull Parcelable.Creator<T> creator) {
- assertNotRecycled();
final int count = readInt();
if (count < 0) {
return null;
@@ -4144,7 +3975,6 @@
*/
@Nullable
public final ArrayList<String> createStringArrayList() {
- assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -4171,7 +4001,6 @@
*/
@Nullable
public final ArrayList<IBinder> createBinderArrayList() {
- assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -4199,7 +4028,6 @@
@Nullable
public final <T extends IInterface> ArrayList<T> createInterfaceArrayList(
@NonNull Function<IBinder, T> asInterface) {
- assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -4220,7 +4048,6 @@
* @see #writeStringList
*/
public final void readStringList(@NonNull List<String> list) {
- assertNotRecycled();
int M = list.size();
int N = readInt();
int i = 0;
@@ -4242,7 +4069,6 @@
* @see #writeBinderList
*/
public final void readBinderList(@NonNull List<IBinder> list) {
- assertNotRecycled();
int M = list.size();
int N = readInt();
int i = 0;
@@ -4265,7 +4091,6 @@
*/
public final <T extends IInterface> void readInterfaceList(@NonNull List<T> list,
@NonNull Function<IBinder, T> asInterface) {
- assertNotRecycled();
int M = list.size();
int N = readInt();
int i = 0;
@@ -4297,7 +4122,6 @@
@NonNull
public final <T extends Parcelable> List<T> readParcelableList(@NonNull List<T> list,
@Nullable ClassLoader cl) {
- assertNotRecycled();
return readParcelableListInternal(list, cl, /*clazz*/ null);
}
@@ -4319,7 +4143,6 @@
@NonNull
public <T> List<T> readParcelableList(@NonNull List<T> list,
@Nullable ClassLoader cl, @NonNull Class<? extends T> clazz) {
- assertNotRecycled();
Objects.requireNonNull(list);
Objects.requireNonNull(clazz);
return readParcelableListInternal(list, cl, clazz);
@@ -4365,7 +4188,6 @@
*/
@Nullable
public final <T> T[] createTypedArray(@NonNull Parcelable.Creator<T> c) {
- assertNotRecycled();
int N = readInt();
if (N < 0) {
return null;
@@ -4379,7 +4201,6 @@
}
public final <T> void readTypedArray(@NonNull T[] val, @NonNull Parcelable.Creator<T> c) {
- assertNotRecycled();
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
@@ -4396,7 +4217,6 @@
*/
@Deprecated
public final <T> T[] readTypedArray(Parcelable.Creator<T> c) {
- assertNotRecycled();
return createTypedArray(c);
}
@@ -4413,7 +4233,6 @@
*/
@Nullable
public final <T> T readTypedObject(@NonNull Parcelable.Creator<T> c) {
- assertNotRecycled();
if (readInt() != 0) {
return c.createFromParcel(this);
} else {
@@ -4440,7 +4259,6 @@
* @see #readTypedArray
*/
public <T> void readFixedArray(@NonNull T val) {
- assertNotRecycled();
Class<?> componentType = val.getClass().getComponentType();
if (componentType == boolean.class) {
readBooleanArray((boolean[]) val);
@@ -4481,7 +4299,6 @@
*/
public <T, S extends IInterface> void readFixedArray(@NonNull T val,
@NonNull Function<IBinder, S> asInterface) {
- assertNotRecycled();
Class<?> componentType = val.getClass().getComponentType();
if (IInterface.class.isAssignableFrom(componentType)) {
readInterfaceArray((S[]) val, asInterface);
@@ -4508,7 +4325,6 @@
*/
public <T, S extends Parcelable> void readFixedArray(@NonNull T val,
@NonNull Parcelable.Creator<S> c) {
- assertNotRecycled();
Class<?> componentType = val.getClass().getComponentType();
if (Parcelable.class.isAssignableFrom(componentType)) {
readTypedArray((S[]) val, c);
@@ -4566,7 +4382,6 @@
*/
@Nullable
public <T> T createFixedArray(@NonNull Class<T> cls, @NonNull int... dimensions) {
- assertNotRecycled();
// Check if type matches with dimensions
// If type is one-dimensional array, delegate to other creators
// Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
@@ -4640,7 +4455,6 @@
@Nullable
public <T, S extends IInterface> T createFixedArray(@NonNull Class<T> cls,
@NonNull Function<IBinder, S> asInterface, @NonNull int... dimensions) {
- assertNotRecycled();
// Check if type matches with dimensions
// If type is one-dimensional array, delegate to other creators
// Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
@@ -4701,7 +4515,6 @@
@Nullable
public <T, S extends Parcelable> T createFixedArray(@NonNull Class<T> cls,
@NonNull Parcelable.Creator<S> c, @NonNull int... dimensions) {
- assertNotRecycled();
// Check if type matches with dimensions
// If type is one-dimensional array, delegate to other creators
// Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
@@ -4765,7 +4578,6 @@
*/
public final <T extends Parcelable> void writeParcelableArray(@Nullable T[] value,
int parcelableFlags) {
- assertNotRecycled();
if (value != null) {
int N = value.length;
writeInt(N);
@@ -4784,7 +4596,6 @@
*/
@Nullable
public final Object readValue(@Nullable ClassLoader loader) {
- assertNotRecycled();
return readValue(loader, /* clazz */ null);
}
@@ -4840,7 +4651,6 @@
*/
@Nullable
public Object readLazyValue(@Nullable ClassLoader loader) {
- assertNotRecycled();
int start = dataPosition();
int type = readInt();
if (isLengthPrefixed(type)) {
@@ -5243,7 +5053,6 @@
@Deprecated
@Nullable
public final <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader) {
- assertNotRecycled();
return readParcelableInternal(loader, /* clazz */ null);
}
@@ -5263,7 +5072,6 @@
*/
@Nullable
public <T> T readParcelable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
- assertNotRecycled();
Objects.requireNonNull(clazz);
return readParcelableInternal(loader, clazz);
}
@@ -5292,7 +5100,6 @@
@Nullable
public final <T extends Parcelable> T readCreator(@NonNull Parcelable.Creator<?> creator,
@Nullable ClassLoader loader) {
- assertNotRecycled();
if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
Parcelable.ClassLoaderCreator<?> classLoaderCreator =
(Parcelable.ClassLoaderCreator<?>) creator;
@@ -5320,7 +5127,6 @@
@Deprecated
@Nullable
public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) {
- assertNotRecycled();
return readParcelableCreatorInternal(loader, /* clazz */ null);
}
@@ -5341,7 +5147,6 @@
@Nullable
public <T> Parcelable.Creator<T> readParcelableCreator(
@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
- assertNotRecycled();
Objects.requireNonNull(clazz);
return readParcelableCreatorInternal(loader, clazz);
}
@@ -5464,7 +5269,6 @@
@Deprecated
@Nullable
public Parcelable[] readParcelableArray(@Nullable ClassLoader loader) {
- assertNotRecycled();
return readParcelableArrayInternal(loader, /* clazz */ null);
}
@@ -5485,7 +5289,6 @@
@SuppressLint({"ArrayReturn", "NullableCollection"})
@Nullable
public <T> T[] readParcelableArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
- assertNotRecycled();
return readParcelableArrayInternal(loader, requireNonNull(clazz));
}
@@ -5519,7 +5322,6 @@
@Deprecated
@Nullable
public Serializable readSerializable() {
- assertNotRecycled();
return readSerializableInternal(/* loader */ null, /* clazz */ null);
}
@@ -5536,7 +5338,6 @@
*/
@Nullable
public <T> T readSerializable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
- assertNotRecycled();
Objects.requireNonNull(clazz);
return readSerializableInternal(
loader == null ? getClass().getClassLoader() : loader, clazz);
@@ -5778,7 +5579,6 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void readArrayMap(@NonNull ArrayMap<? super String, Object> outVal,
@Nullable ClassLoader loader) {
- assertNotRecycled();
final int N = readInt();
if (N < 0) {
return;
@@ -5795,7 +5595,6 @@
*/
@UnsupportedAppUsage
public @Nullable ArraySet<? extends Object> readArraySet(@Nullable ClassLoader loader) {
- assertNotRecycled();
final int size = readInt();
if (size < 0) {
return null;
@@ -5935,7 +5734,6 @@
* @hide For testing
*/
public long getOpenAshmemSize() {
- assertNotRecycled();
return nativeGetOpenAshmemSize(mNativePtr);
}
diff --git a/core/java/android/os/SELinux.java b/core/java/android/os/SELinux.java
index f64a811..11c54ef 100644
--- a/core/java/android/os/SELinux.java
+++ b/core/java/android/os/SELinux.java
@@ -193,4 +193,31 @@
return false;
}
}
+
+ /**
+ * Gets the genfs labels version of the vendor. The genfs labels version is
+ * specified in {@code /vendor/etc/selinux/genfs_labels_version.txt}. The
+ * version follows the VINTF version format "YYYYMM" and affects how {@code
+ * genfs_contexts} entries are applied.
+ *
+ * <p>The genfs labels version indicates changes in the SELinux labeling
+ * scheme over time. For example:
+ * <ul>
+ * <li>For version 202504 and later, {@code /sys/class/udc} is labeled as
+ * {@code sysfs_udc}.
+ * <li>For version 202404 and earlier, {@code /sys/class/udc} is labeled
+ * as {@code sysfs}.
+ * </ul>
+ * Check {@code /system/etc/selinux/plat_sepolicy_genfs_{version}.cil} to
+ * see which labels are new in {version}.
+ *
+ * <p>Older vendors may override {@code genfs_contexts} with vendor-specific
+ * extensions. The framework must not break such labellings to maintain
+ * compatibility with such vendors, by checking the genfs labels version and
+ * implementing a fallback mechanism.
+ *
+ * @return an integer representing the genfs labels version of /vendor, in
+ * the format YYYYMM.
+ */
+ public static final native int getGenfsLabelsVersion();
}
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index 8aec7eb..9085fe0 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -277,7 +277,8 @@
if (service != null) {
return service;
} else {
- return Binder.allowBlocking(getIServiceManager().checkService(name).getBinder());
+ return Binder.allowBlocking(
+ getIServiceManager().checkService(name).getServiceWithMetadata().service);
}
} catch (RemoteException e) {
Log.e(TAG, "error in checkService", e);
@@ -425,7 +426,8 @@
private static IBinder rawGetService(String name) throws RemoteException {
final long start = sStatLogger.getTime();
- final IBinder binder = getIServiceManager().getService2(name).getBinder();
+ final IBinder binder =
+ getIServiceManager().getService2(name).getServiceWithMetadata().service;
final int time = (int) sStatLogger.logDurationStat(Stats.GET_SERVICE, start);
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index 5a9c878..49b696d 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -61,7 +61,7 @@
@UnsupportedAppUsage
public IBinder getService(String name) throws RemoteException {
// Same as checkService (old versions of servicemanager had both methods).
- return checkService(name).getBinder();
+ return checkService(name).getServiceWithMetadata().service;
}
public Service getService2(String name) throws RemoteException {
diff --git a/core/java/android/os/TestLooperManager.java b/core/java/android/os/TestLooperManager.java
index 4b16c1d..6431f3c 100644
--- a/core/java/android/os/TestLooperManager.java
+++ b/core/java/android/os/TestLooperManager.java
@@ -14,6 +14,8 @@
package android.os;
+import android.annotation.FlaggedApi;
+import android.annotation.Nullable;
import android.util.ArraySet;
import java.util.concurrent.LinkedBlockingQueue;
@@ -93,9 +95,48 @@
}
/**
- * Releases the looper to continue standard looping and processing of messages,
- * no further interactions with TestLooperManager will be allowed after
- * release() has been called.
+ * Returns the next message that should be executed by this queue, and removes it from the
+ * queue. If the queue is empty or no messages are deliverable, returns null.
+ * This method never blocks.
+ *
+ * <p>Callers should always call {@link #recycle(Message)} on the message when all interactions
+ * with it have completed.
+ */
+ @FlaggedApi(Flags.FLAG_MESSAGE_QUEUE_TESTABILITY)
+ @Nullable
+ public Message pop() {
+ checkReleased();
+ return mQueue.popForTest();
+ }
+
+ /**
+ * Returns the values of {@link Message#when} of the next message that should be executed by
+ * this queue. If the queue is empty or no messages are deliverable, returns null.
+ * This method never blocks.
+ */
+ @FlaggedApi(Flags.FLAG_MESSAGE_QUEUE_TESTABILITY)
+ @SuppressWarnings("AutoBoxing") // box the primitive long, or return null to indicate no value
+ @Nullable
+ public Long peekWhen() {
+ checkReleased();
+ return mQueue.peekWhenForTest();
+ }
+
+ /**
+ * Checks whether the Looper is currently blocked on a sync barrier.
+ *
+ * A Looper is blocked on a sync barrier if there is a Message in the Looper's
+ * queue that is ready for execution but is behind a sync barrier
+ */
+ @FlaggedApi(Flags.FLAG_MESSAGE_QUEUE_TESTABILITY)
+ public boolean isBlockedOnSyncBarrier() {
+ checkReleased();
+ return mQueue.isBlockedOnSyncBarrier();
+ }
+
+ /**
+ * Releases the looper to continue standard looping and processing of messages, no further
+ * interactions with TestLooperManager will be allowed after release() has been called.
*/
public void release() {
synchronized (sHeldLoopers) {
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index 0a8f62f..81e4549 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -667,4 +668,23 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Run postinstall script for specified partition |partition|
+ *
+ * @param partition The partition to trigger postinstall runs
+ *
+ * @throws ServiceSpecificException error code of this exception would be one of
+ * https://cs.android.com/android/platform/superproject/main/+/main:system/update_engine/common/error_code.h
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_UPDATE_ENGINE_API)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public void triggerPostinstall(@NonNull String partition) {
+ try {
+ mUpdateEngine.triggerPostinstall(partition);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 6357baa..2a46738 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -233,6 +233,14 @@
}
flag {
+ name: "material_shape_tokens"
+ namespace: "systemui"
+ description: "Adding new Material Tokens for M3 Shape (corner radius) Spec"
+ bug: "324928718"
+ is_exported: true
+}
+
+flag {
name: "message_queue_tail_tracking"
namespace: "system_performance"
description: "track tail of message queue."
diff --git a/core/java/android/os/instrumentation/IDynamicInstrumentationManager.aidl b/core/java/android/os/instrumentation/IDynamicInstrumentationManager.aidl
index c45c51d..af56bfe 100644
--- a/core/java/android/os/instrumentation/IDynamicInstrumentationManager.aidl
+++ b/core/java/android/os/instrumentation/IDynamicInstrumentationManager.aidl
@@ -16,7 +16,7 @@
package android.os.instrumentation;
-import android.os.instrumentation.ExecutableMethodFileOffsets;
+import android.os.instrumentation.IOffsetCallback;
import android.os.instrumentation.MethodDescriptor;
import android.os.instrumentation.TargetProcess;
@@ -28,6 +28,7 @@
interface IDynamicInstrumentationManager {
/** Provides ART metadata about the described compiled method within the target process */
@PermissionManuallyEnforced
- @nullable ExecutableMethodFileOffsets getExecutableMethodFileOffsets(
- in TargetProcess targetProcess, in MethodDescriptor methodDescriptor);
+ void getExecutableMethodFileOffsets(
+ in TargetProcess targetProcess, in MethodDescriptor methodDescriptor,
+ in IOffsetCallback callback);
}
diff --git a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl b/core/java/android/os/instrumentation/IOffsetCallback.aidl
similarity index 61%
copy from core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
copy to core/java/android/os/instrumentation/IOffsetCallback.aidl
index 0589bf8..a28c93f 100644
--- a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
+++ b/core/java/android/os/instrumentation/IOffsetCallback.aidl
@@ -5,7 +5,7 @@
* you may not use this 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,
@@ -14,9 +14,15 @@
* limitations under the License.
*/
-package android.app.ondeviceintelligence;
+package android.os.instrumentation;
+
+import android.os.instrumentation.ExecutableMethodFileOffsets;
/**
- * @hide
- */
-parcelable FeatureDetails;
+ * System private API for providing dynamic instrumentation offset results.
+ *
+ * {@hide}
+ */
+oneway interface IOffsetCallback {
+ void onResult(in @nullable ExecutableMethodFileOffsets offsets);
+}
diff --git a/core/java/android/os/instrumentation/MethodDescriptorParser.java b/core/java/android/os/instrumentation/MethodDescriptorParser.java
new file mode 100644
index 0000000..57fc44f
--- /dev/null
+++ b/core/java/android/os/instrumentation/MethodDescriptorParser.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.os.instrumentation;
+
+import android.annotation.NonNull;
+
+import java.lang.reflect.Method;
+
+/**
+ * A utility class for dynamic instrumentation / uprobestats.
+ *
+ * @hide
+ */
+public final class MethodDescriptorParser {
+
+ /**
+ * Parses a {@link MethodDescriptor} (in string representation) into a {@link Method}.
+ */
+ public static Method parseMethodDescriptor(ClassLoader classLoader,
+ @NonNull MethodDescriptor descriptor) {
+ try {
+ Class<?> javaClass = classLoader.loadClass(descriptor.fullyQualifiedClassName);
+ Class<?>[] parameters = new Class[descriptor.fullyQualifiedParameters.length];
+ for (int i = 0; i < descriptor.fullyQualifiedParameters.length; i++) {
+ String typeName = descriptor.fullyQualifiedParameters[i];
+ boolean isArrayType = typeName.endsWith("[]");
+ if (isArrayType) {
+ typeName = typeName.substring(0, typeName.length() - 2);
+ }
+ switch (typeName) {
+ case "boolean":
+ parameters[i] = isArrayType ? boolean.class.arrayType() : boolean.class;
+ break;
+ case "byte":
+ parameters[i] = isArrayType ? byte.class.arrayType() : byte.class;
+ break;
+ case "char":
+ parameters[i] = isArrayType ? char.class.arrayType() : char.class;
+ break;
+ case "short":
+ parameters[i] = isArrayType ? short.class.arrayType() : short.class;
+ break;
+ case "int":
+ parameters[i] = isArrayType ? int.class.arrayType() : int.class;
+ break;
+ case "long":
+ parameters[i] = isArrayType ? long.class.arrayType() : long.class;
+ break;
+ case "float":
+ parameters[i] = isArrayType ? float.class.arrayType() : float.class;
+ break;
+ case "double":
+ parameters[i] = isArrayType ? double.class.arrayType() : double.class;
+ break;
+ default:
+ parameters[i] = isArrayType ? classLoader.loadClass(typeName).arrayType()
+ : classLoader.loadClass(typeName);
+ }
+ }
+
+ return javaClass.getDeclaredMethod(descriptor.methodName, parameters);
+ } catch (ClassNotFoundException | NoSuchMethodException e) {
+ throw new IllegalArgumentException(
+ "The specified method cannot be found. Is this descriptor valid? "
+ + descriptor, e);
+ }
+ }
+}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index e98397d..2473de4 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1795,14 +1795,45 @@
}
}
- /** @hide */
- public static final String CACHE_KEY_PACKAGE_INFO =
+ // The legacy system property "package_info" had two purposes: to invalidate PIC caches and to
+ // signal that package information, and therefore permissions, might have changed.
+ // AudioSystem is the only client of the signaling behavior. The "separate permissions
+ // notification" feature splits the two behaviors into two system property names.
+ //
+ // If the feature is disabled (legacy behavior) then the two system property names have the
+ // same value. This means there is only one system property in use.
+ //
+ // If the feature is enabled, then the two system property names have different values, which
+ // means there is a system property used by PIC and a system property used for signaling. The
+ // legacy value is hard-coded in native code that relies on the signaling behavior, so the
+ // system property name for signaling is the legacy property name, and the system property
+ // name for PIC is new.
+ private static String getPackageInfoCacheKey() {
+ if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
+ return PropertyInvalidatedCache.createSystemCacheKey("package_info_cache");
+ } else {
+ return CACHE_KEY_PACKAGE_INFO_NOTIFY;
+ }
+ }
+
+ /**
+ * The system property that is used to notify clients that package information, and therefore
+ * permissions, may have changed.
+ * @hide
+ */
+ public static final String CACHE_KEY_PACKAGE_INFO_NOTIFY =
PropertyInvalidatedCache.createSystemCacheKey("package_info");
+ /**
+ * The system property that is used to invalidate PIC caches.
+ * @hide
+ */
+ public static final String CACHE_KEY_PACKAGE_INFO_CACHE = getPackageInfoCacheKey();
+
/** @hide */
private static final PropertyInvalidatedCache<PermissionQuery, Integer> sPermissionCache =
new PropertyInvalidatedCache<PermissionQuery, Integer>(
- 2048, CACHE_KEY_PACKAGE_INFO, "checkPermission") {
+ 2048, CACHE_KEY_PACKAGE_INFO_CACHE, "checkPermission") {
@Override
public Integer recompute(PermissionQuery query) {
return checkPermissionUncached(query.permission, query.pid, query.uid,
@@ -1920,7 +1951,7 @@
private static PropertyInvalidatedCache<PackageNamePermissionQuery, Integer>
sPackageNamePermissionCache =
new PropertyInvalidatedCache<PackageNamePermissionQuery, Integer>(
- 16, CACHE_KEY_PACKAGE_INFO, "checkPackageNamePermission") {
+ 16, CACHE_KEY_PACKAGE_INFO_CACHE, "checkPackageNamePermission") {
@Override
public Integer recompute(PackageNamePermissionQuery query) {
return checkPackageNamePermissionUncached(
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 57e1b58..aacc6e2 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -483,3 +483,22 @@
description: "Rate limit async noteOp callbacks for batched noteOperation binder call"
bug: "366013082"
}
+
+flag {
+ name: "system_vendor_intelligence_role_enabled"
+ is_exported: true
+ is_fixed_read_only: true
+ namespace: "permissions"
+ description: "This flag is used to enable the role system_vendor_intelligence"
+ bug: "377553620"
+}
+
+flag {
+ name: "fine_power_monitor_permission"
+ is_fixed_read_only: true
+ is_exported: true
+ namespace: "permissions"
+ description: "Add support for fine-grained PowerMonitor readings"
+ bug: "341941666"
+}
+
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index e4a3c9f..25e8a4d 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -213,7 +213,10 @@
private int mPreferenceHeaderItemResId = 0;
private boolean mPreferenceHeaderRemoveEmptyIcon = false;
+ private boolean mIsBackCallbackRegistered = false;
private final OnBackInvokedCallback mOnBackInvokedCallback = this::onBackInvoked;
+ private final FragmentManager.OnBackStackChangedListener mOnBackStackChangedListener =
+ this::updateBackCallbackRegistrationState;
/**
* The starting request code given out to preference framework.
@@ -706,6 +709,7 @@
}
}
updateBackCallbackRegistrationState();
+ getFragmentManager().addOnBackStackChangedListener(mOnBackStackChangedListener);
}
@Override
@@ -715,17 +719,25 @@
private void updateBackCallbackRegistrationState() {
if (!WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(this)) return;
- if (mCurHeader != null && mSinglePane && getFragmentManager().getBackStackEntryCount() == 0
- && getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT) == null) {
- getOnBackInvokedDispatcher()
- .registerOnBackInvokedCallback(PRIORITY_DEFAULT, mOnBackInvokedCallback);
- } else {
+ if ((mCurHeader != null && getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT) == null
+ && mSinglePane) || getFragmentManager().getBackStackEntryCount() != 0) {
+ if (!mIsBackCallbackRegistered) {
+ getOnBackInvokedDispatcher()
+ .registerOnBackInvokedCallback(PRIORITY_DEFAULT, mOnBackInvokedCallback);
+ mIsBackCallbackRegistered = true;
+ }
+ } else if (mIsBackCallbackRegistered) {
getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mOnBackInvokedCallback);
+ mIsBackCallbackRegistered = false;
}
}
private void onBackInvoked() {
- if (mCurHeader != null && mSinglePane && getFragmentManager().getBackStackEntryCount() == 0
+ if (WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(this)
+ && getFragmentManager().getBackStackEntryCount() != 0) {
+ getFragmentManager().popBackStackImmediate();
+ } else if (mCurHeader != null && mSinglePane
+ && getFragmentManager().getBackStackEntryCount() == 0
&& getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT) == null) {
mCurHeader = null;
@@ -1012,6 +1024,7 @@
@Override
protected void onDestroy() {
+ getFragmentManager().removeOnBackStackChangedListener(mOnBackStackChangedListener);
mHandler.removeMessages(MSG_BIND_PREFERENCES);
mHandler.removeMessages(MSG_BUILD_HEADERS);
super.onDestroy();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0ae9ffa..4acb631 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2469,6 +2469,25 @@
= "android.settings.APP_NOTIFICATION_BUBBLE_SETTINGS";
/**
+ * Activity Action: Show the settings for users to select their preferred SIM subscription
+ * when a new SIM subscription has become available.
+ * <p>
+ * This Activity will only launch successfully if the newly active subscription ID is set as the
+ * value of {@link EXTRA_SUB_ID} and the value corresponds with an active SIM subscription.
+ * <p>
+ * Input: {@link #EXTRA_SUB_ID}: the subscription ID of the newly active SIM subscription.
+ * <p>
+ * Output: Nothing.
+ *
+ * @hide
+ */
+ @FlaggedApi(com.android.internal.telephony.flags.Flags.FLAG_ACTION_SIM_PREFERENCE_SETTINGS)
+ @SystemApi
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_SIM_PREFERENCE_SETTINGS =
+ "android.settings.SIM_PREFERENCE_SETTINGS";
+
+ /**
* Intent Extra: The value of {@link android.app.settings.SettingsEnums#EntryPointType} for
* settings metrics that logs the entry point about physical keyboard settings.
* <p>
@@ -10023,6 +10042,12 @@
"minimal_post_processing_allowed";
/**
+ * Whether to mirror the built-in display on all connected displays.
+ * @hide
+ */
+ public static final String MIRROR_BUILT_IN_DISPLAY = "mirror_built_in_display";
+
+ /**
* No mode switching will happen.
*
* @see #MATCH_CONTENT_FRAME_RATE
diff --git a/core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.java b/core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.java
index 64a3f0f..568a6d7 100644
--- a/core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.java
+++ b/core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.java
@@ -38,23 +38,30 @@
/**
* Client message associated with the request to disable secure lock on the device. This message
* will be shown on the device when secure lock mode is disabled.
+ *
+ * Since this text is shown in a restricted lockscreen state, typeface properties such as color,
+ * font weight, or other formatting may not be honored.
*/
- private final @NonNull String mMessage;
+ private final @NonNull CharSequence mMessage;
/**
* Creates DisableSecureLockDeviceParams with the given params.
*
* @param message Allows clients to pass in a message with information about the request to
* disable secure lock on the device. This message will be shown to the user when
- * secure lock mode is disabled. If an empty string is provided, it will default
- * to a system-defined string (e.g. "Secure lock mode has been disabled.")
+ * secure lock mode is disabled. If an empty CharSequence is provided, it will
+ * default to a system-defined CharSequence (e.g. "Secure lock mode has been
+ * disabled.")
+ *
+ * Since this text is shown in a restricted lockscreen state, typeface properties
+ * such as color, font weight, or other formatting may not be honored.
*/
- public DisableSecureLockDeviceParams(@NonNull String message) {
+ public DisableSecureLockDeviceParams(@NonNull CharSequence message) {
mMessage = message;
}
private DisableSecureLockDeviceParams(@NonNull Parcel in) {
- mMessage = Objects.requireNonNull(in.readString8());
+ mMessage = Objects.requireNonNull(in.readCharSequence());
}
public static final @NonNull Creator<DisableSecureLockDeviceParams> CREATOR =
@@ -77,6 +84,6 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString8(mMessage);
+ dest.writeCharSequence(mMessage);
}
}
diff --git a/core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.java b/core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.java
index 1d72772..dfa391f 100644
--- a/core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.java
+++ b/core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.java
@@ -38,23 +38,30 @@
/**
* Client message associated with the request to enable secure lock on the device. This message
* will be shown on the device when secure lock mode is enabled.
+ *
+ * Since this text is shown in a restricted lockscreen state, typeface properties such as color,
+ * font weight, or other formatting may not be honored.
*/
- private final @NonNull String mMessage;
+ private final @NonNull CharSequence mMessage;
/**
* Creates EnableSecureLockDeviceParams with the given params.
*
* @param message Allows clients to pass in a message with information about the request to
* enable secure lock on the device. This message will be shown to the user when
- * secure lock mode is enabled. If an empty string is provided, it will default
- * to a system-defined string (e.g. "Device is securely locked remotely.")
+ * secure lock mode is enabled. If an empty CharSequence is provided, it will
+ * default to a system-defined CharSequence (e.g. "Device is securely locked
+ * remotely.")
+ *
+ * Since this text is shown in a restricted lockscreen state, typeface properties
+ * such as color, font weight, or other formatting may not be honored.
*/
- public EnableSecureLockDeviceParams(@NonNull String message) {
+ public EnableSecureLockDeviceParams(@NonNull CharSequence message) {
mMessage = message;
}
private EnableSecureLockDeviceParams(@NonNull Parcel in) {
- mMessage = Objects.requireNonNull(in.readString8());
+ mMessage = Objects.requireNonNull(in.readCharSequence());
}
public static final @NonNull Creator<EnableSecureLockDeviceParams> CREATOR =
@@ -77,6 +84,6 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString8(mMessage);
+ dest.writeCharSequence(mMessage);
}
}
diff --git a/core/java/android/security/net/config/CertificatesEntryRef.java b/core/java/android/security/net/config/CertificatesEntryRef.java
index 45cd0f0..a46049f 100644
--- a/core/java/android/security/net/config/CertificatesEntryRef.java
+++ b/core/java/android/security/net/config/CertificatesEntryRef.java
@@ -17,6 +17,7 @@
package android.security.net.config;
import android.util.ArraySet;
+
import java.security.cert.X509Certificate;
import java.util.Set;
@@ -24,16 +25,23 @@
public final class CertificatesEntryRef {
private final CertificateSource mSource;
private final boolean mOverridesPins;
+ private final boolean mDisableCT;
- public CertificatesEntryRef(CertificateSource source, boolean overridesPins) {
+ public CertificatesEntryRef(CertificateSource source, boolean overridesPins,
+ boolean disableCT) {
mSource = source;
mOverridesPins = overridesPins;
+ mDisableCT = disableCT;
}
boolean overridesPins() {
return mOverridesPins;
}
+ boolean disableCT() {
+ return mDisableCT;
+ }
+
public Set<TrustAnchor> getTrustAnchors() {
// TODO: cache this [but handle mutable sources]
Set<TrustAnchor> anchors = new ArraySet<TrustAnchor>();
diff --git a/core/java/android/security/net/config/KeyStoreConfigSource.java b/core/java/android/security/net/config/KeyStoreConfigSource.java
index 8d4f098..a54d8d0 100644
--- a/core/java/android/security/net/config/KeyStoreConfigSource.java
+++ b/core/java/android/security/net/config/KeyStoreConfigSource.java
@@ -17,8 +17,8 @@
package android.security.net.config;
import android.util.Pair;
+
import java.security.KeyStore;
-import java.security.KeyStoreException;
import java.util.Set;
/**
@@ -32,7 +32,7 @@
mConfig = new NetworkSecurityConfig.Builder()
.addCertificatesEntryRef(
// Use the KeyStore and do not override pins (of which there are none).
- new CertificatesEntryRef(new KeyStoreCertificateSource(ks), false))
+ new CertificatesEntryRef(new KeyStoreCertificateSource(ks), false, false))
.build();
}
diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java
index 129ae63..410c68b 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfig.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfig.java
@@ -112,7 +112,6 @@
return mHstsEnforced;
}
- // TODO(b/28746284): add exceptions for user-added certificates and enterprise overrides.
public boolean isCertificateTransparencyVerificationRequired() {
return mCertificateTransparencyVerificationRequired;
}
@@ -192,20 +191,21 @@
* @hide
*/
public static Builder getDefaultBuilder(ApplicationInfo info) {
+ // System certificate store, does not bypass static pins, does not disable CT.
+ CertificatesEntryRef systemRef = new CertificatesEntryRef(
+ SystemCertificateSource.getInstance(), false, false);
Builder builder = new Builder()
.setHstsEnforced(DEFAULT_HSTS_ENFORCED)
- // System certificate store, does not bypass static pins.
- .addCertificatesEntryRef(
- new CertificatesEntryRef(SystemCertificateSource.getInstance(), false));
+ .addCertificatesEntryRef(systemRef);
final boolean cleartextTrafficPermitted = info.targetSdkVersion < Build.VERSION_CODES.P
&& !info.isInstantApp();
builder.setCleartextTrafficPermitted(cleartextTrafficPermitted);
// Applications targeting N and above must opt in into trusting the user added certificate
// store.
if (info.targetSdkVersion <= Build.VERSION_CODES.M && !info.isPrivilegedApp()) {
- // User certificate store, does not bypass static pins.
+ // User certificate store, does not bypass static pins. CT is disabled.
builder.addCertificatesEntryRef(
- new CertificatesEntryRef(UserCertificateSource.getInstance(), false));
+ new CertificatesEntryRef(UserCertificateSource.getInstance(), false, true));
}
return builder;
}
@@ -339,6 +339,16 @@
if (mCertificateTransparencyVerificationRequiredSet) {
return mCertificateTransparencyVerificationRequired;
}
+ // CT verification has not been set explicitly. Before deferring to
+ // the parent, check if any of the CertificatesEntryRef requires it
+ // to be disabled (i.e., user store or inline certificate).
+ if (hasCertificatesEntryRefs()) {
+ for (CertificatesEntryRef ref : getCertificatesEntryRefs()) {
+ if (ref.disableCT()) {
+ return false;
+ }
+ }
+ }
if (mParentBuilder != null) {
return mParentBuilder.getCertificateTransparencyVerificationRequired();
}
diff --git a/core/java/android/security/net/config/XmlConfigSource.java b/core/java/android/security/net/config/XmlConfigSource.java
index b1c1479..95e579f 100644
--- a/core/java/android/security/net/config/XmlConfigSource.java
+++ b/core/java/android/security/net/config/XmlConfigSource.java
@@ -182,6 +182,7 @@
boolean overridePins =
parser.getAttributeBooleanValue(null, "overridePins", defaultOverridePins);
int sourceId = parser.getAttributeResourceValue(null, "src", -1);
+ boolean disableCT = false;
String sourceString = parser.getAttributeValue(null, "src");
CertificateSource source = null;
if (sourceString == null) {
@@ -190,10 +191,12 @@
if (sourceId != -1) {
// TODO: Cache ResourceCertificateSources by sourceId
source = new ResourceCertificateSource(sourceId, mContext);
+ disableCT = true;
} else if ("system".equals(sourceString)) {
source = SystemCertificateSource.getInstance();
} else if ("user".equals(sourceString)) {
source = UserCertificateSource.getInstance();
+ disableCT = true;
} else if ("wfa".equals(sourceString)) {
source = WfaCertificateSource.getInstance();
} else {
@@ -201,7 +204,7 @@
+ "Should be one of system|user|@resourceVal");
}
XmlUtils.skipCurrentTag(parser);
- return new CertificatesEntryRef(source, overridePins);
+ return new CertificatesEntryRef(source, overridePins, disableCT);
}
private Collection<CertificatesEntryRef> parseTrustAnchors(XmlResourceParser parser,
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index 42dbd37..8add9f7 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -97,14 +97,6 @@
}
flag {
- name: "prevent_intent_redirect_show_toast_if_nested_keys_not_collected"
- namespace: "responsible_apis"
- description: "Prevent intent redirect attacks by showing a toast if not yet collected"
- bug: "361143368"
- is_fixed_read_only: true
-}
-
-flag {
name: "prevent_intent_redirect_show_toast_if_nested_keys_not_collected_r_w"
namespace: "responsible_apis"
description: "Prevent intent redirect attacks by showing a toast if not yet collected"
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index fba8e42..6d62a2c 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -341,7 +341,7 @@
/** Credential Manager suggestions are shown instead of Autofill suggestion */
@FlaggedApi(FLAG_AUTOFILL_W_METRICS)
- public static final int UI_TYPE_CREDMAN = 4;
+ public static final int UI_TYPE_CREDENTIAL_MANAGER = 4;
/** @hide */
@IntDef(prefix = { "UI_TYPE_" }, value = {
diff --git a/core/java/android/service/ondeviceintelligence/OWNERS b/core/java/android/service/ondeviceintelligence/OWNERS
deleted file mode 100644
index 09774f7..0000000
--- a/core/java/android/service/ondeviceintelligence/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file:/core/java/android/app/ondeviceintelligence/OWNERS
diff --git a/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java b/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java
index 1acb7b8..ea7d4a6 100644
--- a/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java
+++ b/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java
@@ -187,27 +187,39 @@
/** @hide */
@IntDef(value = {
- NOT_SENSITIVE,
- SENSITIVE,
- INTENT_ONLY
+ NO_SENSITIVITY,
+ EXPECT_POST_CONFIRMATION,
+ EXPECT_PRE_CONFIRMATION,
+ NO_DIRECT_ACCESS,
})
@Retention(RetentionPolicy.SOURCE)
public @interface WriteSensitivity {}
/**
- * Preference is not sensitive, thus its value is writable without explicit consent, assuming
- * all necessary permissions are granted.
+ * Indicates preference is not sensitive.
+ * <p>Its value is writable without explicit consent, assuming all necessary permissions are
+ * granted.
*/
- public static final int NOT_SENSITIVE = 0;
+ public static final int NO_SENSITIVITY = 0;
/**
- * Preference is sensitive, meaning that in addition to necessary permissions, writing its value
- * will also request explicit user consent.
+ * Indicates preference is mildly sensitive.
+ * <p>In addition to necessary permissions, after writing its value the user should be
+ * given the option to revert back.
*/
- public static final int SENSITIVE = 1;
+ public static final int EXPECT_POST_CONFIRMATION = 1;
/**
- * Preference is not permitted for write-access via API and must be changed via Settings page.
+ * Indicates preference is sensitive.
+ * <p>In addition to necessary permissions, the user should be prompted for confirmation prior
+ * to making a change. Otherwise it is suggested to provide a deeplink to the Preference's page
+ * instead, accessible via {@link #getLaunchIntent}.
*/
- public static final int INTENT_ONLY = 2;
+ public static final int EXPECT_PRE_CONFIRMATION = 2;
+ /**
+ * Indicates preference is highly sensitivity and carries significant user-risk.
+ * <p>This Preference cannot be changed through this API and no direct deeplink is available.
+ * Other Metadata is still available.
+ */
+ public static final int NO_DIRECT_ACCESS = 3;
private SettingsPreferenceMetadata(@NonNull Builder builder) {
mKey = builder.mKey;
@@ -303,7 +315,7 @@
private boolean mAvailable = false;
private boolean mWritable = false;
private boolean mRestricted = false;
- @WriteSensitivity private int mSensitivity = INTENT_ONLY;
+ @WriteSensitivity private int mSensitivity = NO_DIRECT_ACCESS;
private Intent mLaunchIntent;
private Bundle mExtras;
@@ -436,6 +448,9 @@
*/
@NonNull
public SettingsPreferenceMetadata build() {
+ if (mSensitivity == NO_DIRECT_ACCESS) {
+ mLaunchIntent = null;
+ }
return new SettingsPreferenceMetadata(this);
}
}
diff --git a/core/java/android/speech/tts/EventLogTags.logtags b/core/java/android/speech/tts/EventLogTags.logtags
index e209a28..5ba2bae 100644
--- a/core/java/android/speech/tts/EventLogTags.logtags
+++ b/core/java/android/speech/tts/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package android.speech.tts;
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index c9d560c..802bddd 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1301,17 +1301,23 @@
}
/**
- * Represents the {@link FrameRateCategory} for the Normal frame rate
+ * Normal category determines the framework's recommended normal frame rate.
+ * Opt for this normal rate unless a higher frame rate significantly enhances
+ * the user experience.
*
- * @see FrameRateCategory
+ * @see #getSuggestedFrameRate(int)
+ * @see #FRAME_RATE_CATEGORY_HIGH
*/
@FlaggedApi(FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE)
public static final int FRAME_RATE_CATEGORY_NORMAL = 0;
/**
- * Represents the {@link FrameRateCategory} for the High frame rate
+ * High category determines the framework's recommended high frame rate.
+ * Opt for this high rate when a higher frame rate significantly enhances
+ * the user experience.
*
- * @see FrameRateCategory
+ * @see #getSuggestedFrameRate(int)
+ * @see #FRAME_RATE_CATEGORY_NORMAL
*/
@FlaggedApi(FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE)
public static final int FRAME_RATE_CATEGORY_HIGH = 1;
@@ -1332,22 +1338,17 @@
*
* <p> For example, an animation that does not require fast render rates can use
* the {@link #FRAME_RATE_CATEGORY_NORMAL} to get the suggested frame rate.
- * The suggested frame rate then can be used in the
- * {@link Surface.FrameRateParams.Builder#setDesiredRateRange} for desiredMinRate.
*
* <pre>{@code
* float desiredMinRate = display.getSuggestedFrameRate(FRAME_RATE_CATEGORY_NORMAL);
- * Surface.FrameRateParams params = new Surface.FrameRateParams.Builder().
- * setDesiredRateRange(desiredMinRate, Float.MAX).build();
- * surface.setFrameRate(params);
+ * surface.setFrameRate(desiredMinRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
* }</pre>
* </p>
*
* @param category either {@link #FRAME_RATE_CATEGORY_NORMAL}
* or {@link #FRAME_RATE_CATEGORY_HIGH}
*
- * @see Surface#setFrameRate(Surface.FrameRateParams)
- * @see SurfaceControl.Transaction#setFrameRate(SurfaceControl, Surface.FrameRateParams)
+ * @see Surface#setFrameRate(float, int)
* @throws IllegalArgumentException when category is not {@link #FRAME_RATE_CATEGORY_NORMAL}
* or {@link #FRAME_RATE_CATEGORY_HIGH}
*/
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 6d85e75..072a037 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -766,7 +766,7 @@
* container.
*/
@EnforcePermission("MANAGE_APP_TOKENS")
- void updateDisplayWindowRequestedVisibleTypes(int displayId, int requestedVisibleTypes,
+ void updateDisplayWindowRequestedVisibleTypes(int displayId, int visibleTypes, int mask,
in @nullable ImeTracker.Token statsToken);
/**
@@ -933,6 +933,27 @@
void detachWindowContext(IBinder clientToken);
/**
+ * Reparents the {@link android.window.WindowContext} to the
+ * {@link com.android.server.wm.DisplayArea} on another display.
+ * This method also reparent the WindowContext associated WindowToken to another display if
+ * necessary.
+ * <p>
+ * {@code type} and {@code options} must be the same as the previous call of
+ * {@link #attachWindowContextToDisplayArea} on the same Context otherwise this will fail
+ * silently.
+ *
+ * @param appThread the process that the window context is on.
+ * @param clientToken the window context's token
+ * @param type The window type of the WindowContext
+ * @param displayId The new display id this context windows should be parented to
+ * @param options Bundle the context was created with
+ *
+ * @return True if the operation was successful, False otherwise.
+ */
+ boolean reparentWindowContextToDisplayArea(in IApplicationThread appThread,
+ IBinder clientToken, int displayId);
+
+ /**
* Registers a listener, which is to be called whenever cross-window blur is enabled/disabled.
*
* @param listener the listener to be registered
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index b796e0b..ba20839 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -357,6 +357,31 @@
} else if (mTmpFrame.right == relativeFrame.right) {
return Insets.of(0, 0, mTmpFrame.width(), 0);
}
+ } else {
+ // The source doesn't cover the width or the height of relativeFrame, but just parts of
+ // them. Here uses mSideHint to decide which side should be inset.
+ switch (mSideHint) {
+ case SIDE_LEFT:
+ if (mTmpFrame.left == relativeFrame.left) {
+ return Insets.of(mTmpFrame.width(), 0, 0, 0);
+ }
+ break;
+ case SIDE_TOP:
+ if (mTmpFrame.top == relativeFrame.top) {
+ return Insets.of(0, mTmpFrame.height(), 0, 0);
+ }
+ break;
+ case SIDE_RIGHT:
+ if (mTmpFrame.right == relativeFrame.right) {
+ return Insets.of(0, 0, mTmpFrame.width(), 0);
+ }
+ break;
+ case SIDE_BOTTOM:
+ if (mTmpFrame.bottom == relativeFrame.bottom) {
+ return Insets.of(0, 0, 0, mTmpFrame.height());
+ }
+ break;
+ }
}
return Insets.NONE;
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 67050e0..213ece0 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -296,8 +296,8 @@
@SoftInputModeFlags int softInputMode, int windowFlags) {
final int softInputAdjustMode = softInputMode & SOFT_INPUT_MASK_ADJUST;
final int visibleInsetsTypes = softInputAdjustMode != SOFT_INPUT_ADJUST_NOTHING
- ? systemBars() | ime()
- : systemBars();
+ ? systemBars() | displayCutout() | ime()
+ : systemBars() | displayCutout();
@InsetsType int forceConsumingTypes = 0;
Insets insets = Insets.NONE;
for (int i = mSources.size() - 1; i >= 0; i--) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 049189f..d15b0f5 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -17184,7 +17184,7 @@
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
final WindowMetrics metrics = windowManager.getMaximumWindowMetrics();
final Insets insets = metrics.getWindowInsets().getInsets(
- WindowInsets.Type.navigationBars() | WindowInsets.Type.displayCutout());
+ WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
outRect.set(metrics.getBounds());
outRect.inset(insets);
outRect.offsetTo(0, 0);
@@ -28277,14 +28277,28 @@
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
- if (mParent != null && !mParent.isLayoutRequested()) {
- mParent.requestLayout();
+ if (mParent != null) {
+ if (!mParent.isLayoutRequested()) {
+ mParent.requestLayout();
+ } else {
+ clearMeasureCacheOfAncestors();
+ }
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
}
}
+ private void clearMeasureCacheOfAncestors() {
+ ViewParent parent = mParent;
+ while (parent instanceof View view) {
+ if (view.mMeasureCache != null) {
+ view.mMeasureCache.clear();
+ }
+ parent = view.mParent;
+ }
+ }
+
/**
* Forces this view to be laid out during the next layout pass.
* This method does not call requestLayout() or forceLayout()
@@ -28640,8 +28654,10 @@
*/
@RemotableViewMethod
public void setMinimumHeight(int minHeight) {
- mMinHeight = minHeight;
- requestLayout();
+ if (mMinHeight != minHeight) {
+ mMinHeight = minHeight;
+ requestLayout();
+ }
}
/**
@@ -28671,8 +28687,10 @@
*/
@RemotableViewMethod
public void setMinimumWidth(int minWidth) {
- mMinWidth = minWidth;
- requestLayout();
+ if (mMinWidth != minWidth) {
+ mMinWidth = minWidth;
+ requestLayout();
+ }
}
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 63bf392..9e97a8e 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -591,7 +591,7 @@
res.getBoolean(
com.android.internal.R.bool.config_viewBasedRotaryEncoderHapticsEnabled);
mViewTouchScreenHapticScrollFeedbackEnabled =
- Flags.enableTouchScrollFeedback()
+ Flags.enableScrollFeedbackForTouch()
? res.getBoolean(
com.android.internal.R.bool
.config_viewTouchScreenHapticScrollFeedbackEnabled)
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e9c2cf8..1596b85 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2254,7 +2254,7 @@
onClientWindowFramesChanged(frames);
- CompatibilityInfo.applyOverrideScaleIfNeeded(mergedConfiguration);
+ CompatibilityInfo.applyOverrideIfNeeded(mergedConfiguration);
final Rect frame = frames.frame;
final Rect displayFrame = frames.displayFrame;
final Rect attachedFrame = frames.attachedFrame;
@@ -9458,7 +9458,7 @@
mTranslator.translateRectInScreenToAppWindow(mTmpFrames.attachedFrame);
}
mInvCompatScale = 1f / mTmpFrames.compatScale;
- CompatibilityInfo.applyOverrideScaleIfNeeded(mPendingMergedConfiguration);
+ CompatibilityInfo.applyOverrideIfNeeded(mPendingMergedConfiguration);
handleInsetsControlChanged(mTempInsets, mTempControls);
mPendingAlwaysConsumeSystemBars =
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 1be7f48..43a946a 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -87,8 +87,12 @@
* <p>This value is added to mainly help with debugging purpose.
*/
@FlaggedApi(FLAG_AUTOFILL_W_METRICS)
+ @SuppressWarnings(
+ "ActionValue") // Lint expects this as
+ // android.view.contentcapture.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER
+ // but should not have contentcapture
public static final String EXTRA_VIRTUAL_STRUCTURE_TYPE =
- "android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_TYPE";
+ "android.view.extra.VIRTUAL_STRUCTURE_TYPE";
/**
* Key used for specifying the version of the view that generated the virtual structure for
@@ -98,8 +102,12 @@
* "104.0.5112.69", then the value should be "104.0.5112.69"
*/
@FlaggedApi(FLAG_AUTOFILL_W_METRICS)
+ @SuppressWarnings(
+ "ActionValue") // Lint expects this as
+ // android.view.contentcapture.extra.VIRTUAL_STRUCTURE_TYPE
+ // but should not have contentcapture
public static final String EXTRA_VIRTUAL_STRUCTURE_VERSION_NUMBER =
- "android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER";
+ "android.view.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER";
/**
* Set the identifier for this view.
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index df0c5a3..8a10979 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -6540,6 +6540,15 @@
* Class with information if a node is a range.
*/
public static final class RangeInfo {
+ /** @hide */
+ @IntDef(prefix = { "RANGE_TYPE_" }, value = {
+ RANGE_TYPE_INT,
+ RANGE_TYPE_FLOAT,
+ RANGE_TYPE_PERCENT,
+ RANGE_TYPE_INDETERMINATE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RangeType {}
/** Range type: integer. */
public static final int RANGE_TYPE_INT = 0;
@@ -6588,7 +6597,7 @@
* @param current The current value.
*/
@Deprecated
- public static RangeInfo obtain(int type, float min, float max, float current) {
+ public static RangeInfo obtain(@RangeType int type, float min, float max, float current) {
return new RangeInfo(type, min, max, current);
}
@@ -6602,7 +6611,7 @@
* maximum.
* @param current The current value.
*/
- public RangeInfo(int type, float min, float max, float current) {
+ public RangeInfo(@RangeType int type, float min, float max, float current) {
mType = type;
mMin = min;
mMax = max;
@@ -6618,6 +6627,7 @@
* @see #RANGE_TYPE_FLOAT
* @see #RANGE_TYPE_PERCENT
*/
+ @RangeType
public int getType() {
return mType;
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 1de0474..60e528c 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -26,6 +26,7 @@
import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
import static android.service.autofill.Flags.FLAG_FILL_DIALOG_IMPROVEMENTS;
+import static android.service.autofill.Flags.improveFillDialogAconfig;
import static android.service.autofill.Flags.relayoutFix;
import static android.view.ContentInfo.SOURCE_AUTOFILL;
import static android.view.autofill.Helper.sDebug;
@@ -787,6 +788,11 @@
private AutofillStateFingerprint mAutofillStateFingerprint;
+ /**
+ * Whether improveFillDialog feature is enabled or not.
+ */
+ private boolean mImproveFillDialogEnabled;
+
/** @hide */
public interface AutofillClient {
/**
@@ -1017,6 +1023,17 @@
mRelayoutFix = relayoutFix() && AutofillFeatureFlags.enableRelayoutFixes();
mRelativePositionForRelayout = AutofillFeatureFlags.enableRelativeLocationForRelayout();
mIsCredmanIntegrationEnabled = Flags.autofillCredmanIntegration();
+ mImproveFillDialogEnabled =
+ improveFillDialogAconfig() && AutofillFeatureFlags.isImproveFillDialogEnabled();
+ }
+
+ /**
+ * Whether improvement to fill dialog is enabled.
+ *
+ * @hide
+ */
+ public boolean isImproveFillDialogEnabled() {
+ return mImproveFillDialogEnabled;
}
/**
@@ -1679,6 +1696,11 @@
private void notifyViewReadyInner(AutofillId id, @Nullable String[] autofillHints,
boolean isCredmanRequested) {
+ if (isImproveFillDialogEnabled() && !isCredmanRequested) {
+ // We do not want to send pre-trigger request.
+ // TODO(b/377868687): verify if we can remove the flow for isCredmanRequested too.
+ return;
+ }
if (sDebug) {
Log.d(TAG, "notifyViewReadyInner:" + id);
}
@@ -2046,6 +2068,34 @@
}
/**
+ * Notify autofill system that IME animation has started
+ * @param startTimeMs start time as measured by SystemClock.elapsedRealtime()
+ */
+ void notifyImeAnimationStart(long startTimeMs) {
+ try {
+ mService.notifyImeAnimationStart(mSessionId, startTimeMs, mContext.getUserId());
+ } catch (RemoteException e) {
+ // The failure could be a consequence of something going wrong on the
+ // server side. Just log the exception and move-on.
+ Log.w(TAG, "notifyImeAnimationStart(): RemoteException caught but ignored " + e);
+ }
+ }
+
+ /**
+ * Notify autofill system that IME animation has ended
+ * @param endTimeMs end time as measured by SystemClock.elapsedRealtime()
+ */
+ void notifyImeAnimationEnd(long endTimeMs) {
+ try {
+ mService.notifyImeAnimationEnd(mSessionId, endTimeMs, mContext.getUserId());
+ } catch (RemoteException e) {
+ // The failure could be a consequence of something going wrong on the
+ // server side. Just log the exception and move-on.
+ Log.w(TAG, "notifyImeAnimationStart(): RemoteException caught but ignored " + e);
+ }
+ }
+
+ /**
* Called when a virtual view that supports autofill is exited.
*
* @param view the virtual view parent.
@@ -4050,6 +4100,10 @@
@FlaggedApi(FLAG_FILL_DIALOG_IMPROVEMENTS)
@Deprecated
public boolean showAutofillDialog(@NonNull View view) {
+ if (isImproveFillDialogEnabled()) {
+ Log.i(TAG, "showAutofillDialog() return false due to improve fill dialog");
+ return false;
+ }
Objects.requireNonNull(view);
if (shouldShowAutofillDialog(view, view.getAutofillId())) {
mShowAutofillDialogCalled = true;
@@ -4093,6 +4147,10 @@
@FlaggedApi(FLAG_FILL_DIALOG_IMPROVEMENTS)
@Deprecated
public boolean showAutofillDialog(@NonNull View view, int virtualId) {
+ if (isImproveFillDialogEnabled()) {
+ Log.i(TAG, "showAutofillDialog() return false due to improve fill dialog");
+ return false;
+ }
Objects.requireNonNull(view);
if (shouldShowAutofillDialog(view, getAutofillId(view, virtualId))) {
mShowAutofillDialogCalled = true;
@@ -4117,7 +4175,7 @@
return false;
}
- if (getImeStateFlag(view) == FLAG_IME_SHOWING) {
+ if (getImeStateFlag(view) == FLAG_IME_SHOWING && !isImproveFillDialogEnabled()) {
// IME is showing
return false;
}
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index f67405f..28f8577 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -70,4 +70,6 @@
void notifyNotExpiringResponseDuringAuth(int sessionId, int userId);
void notifyViewEnteredIgnoredDuringAuthCount(int sessionId, int userId);
void setAutofillIdsAttemptedForRefill(int sessionId, in List<AutofillId> ids, int userId);
+ void notifyImeAnimationStart(int sessionId, long startTimeMs, int userId);
+ void notifyImeAnimationEnd(int sessionId, long endTimeMs, int userId);
}
diff --git a/core/java/android/view/flags/scroll_capture.aconfig b/core/java/android/view/flags/scroll_capture.aconfig
index 9080b16..6dccbad 100644
--- a/core/java/android/view/flags/scroll_capture.aconfig
+++ b/core/java/android/view/flags/scroll_capture.aconfig
@@ -11,3 +11,12 @@
}
}
+flag {
+ name: "scroll_capture_relax_scroll_view_criteria"
+ namespace: "systemui"
+ description: "Treat all custom ViewGroups which support scrollTo as ScrollView"
+ bug: "189827634"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/view/flags/scroll_feedback_flags.aconfig b/core/java/android/view/flags/scroll_feedback_flags.aconfig
index ebda4d4..ddf6ff1 100644
--- a/core/java/android/view/flags/scroll_feedback_flags.aconfig
+++ b/core/java/android/view/flags/scroll_feedback_flags.aconfig
@@ -17,10 +17,10 @@
}
flag {
- namespace: "toolkit"
- name: "enable_touch_scroll_feedback"
+ namespace: "wear_frameworks"
+ name: "enable_scroll_feedback_for_touch"
description: "Enables touchscreen haptic scroll feedback"
- bug: "331830899"
+ bug: "382135785"
is_fixed_read_only: true
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 5dd29b2..6d89f3d 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -946,11 +946,16 @@
if (state == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) {
// when losing focus (e.g., by going to another window), we reset the
// requestedVisibleTypes of WindowInsetsController by hiding the IME
+ final var statsToken = ImeTracker.forLogging().onStart(
+ ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_CLIENT,
+ SoftInputShowHideReason.REASON_HIDE_WINDOW_LOST_FOCUS,
+ false /* fromUser */);
if (DEBUG) {
Log.d(TAG, "onWindowLostFocus, hiding IME because "
+ "of STATE_ALWAYS_HIDDEN");
}
- mCurRootView.getInsetsController().hide(WindowInsets.Type.ime());
+ mCurRootView.getInsetsController().hide(WindowInsets.Type.ime(),
+ false /* fromIme */, statsToken);
}
}
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index b606340..b929324 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -16,13 +16,20 @@
package android.view.textclassifier;
+import static android.Manifest.permission.ACCESS_TEXT_CLASSIFIER_BY_TYPE;
+
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Build;
import android.os.ServiceManager;
+import android.permission.flags.Flags;
import android.view.textclassifier.TextClassifier.TextClassifierType;
import com.android.internal.annotations.GuardedBy;
@@ -115,6 +122,29 @@
}
}
+ /**
+ * Returns a specific type of text classifier.
+ * If the specified text classifier cannot be found, this returns {@link TextClassifier#NO_OP}.
+ * <p>
+ *
+ * @see TextClassifier#CLASSIFIER_TYPE_SELF_PROVIDED
+ * @see TextClassifier#CLASSIFIER_TYPE_DEVICE_DEFAULT
+ * @see TextClassifier#CLASSIFIER_TYPE_ANDROID_DEFAULT
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ @FlaggedApi(Flags.FLAG_TEXT_CLASSIFIER_CHOICE_API_ENABLED)
+ @RequiresPermission(ACCESS_TEXT_CLASSIFIER_BY_TYPE)
+ public TextClassifier getClassifier(@TextClassifierType int type) {
+ if (mContext.checkCallingOrSelfPermission(ACCESS_TEXT_CLASSIFIER_BY_TYPE)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Caller does not have permission " + ACCESS_TEXT_CLASSIFIER_BY_TYPE);
+ }
+ return getTextClassifier(type);
+ }
+
private TextClassificationConstants getSettings() {
synchronized (mLock) {
if (mSettings == null) {
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index ef50045..59afdac 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -16,16 +16,20 @@
package android.view.textclassifier;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.annotation.WorkerThread;
import android.os.LocaleList;
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
+import android.permission.flags.Flags;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.URLSpan;
@@ -63,11 +67,6 @@
/** @hide */
String LOG_TAG = "androidtc";
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {LOCAL, SYSTEM, DEFAULT_SYSTEM})
- @interface TextClassifierType {} // TODO: Expose as system APIs.
/** Specifies a TextClassifier that runs locally in the app's process. @hide */
int LOCAL = 0;
/** Specifies a TextClassifier that runs in the system process and serves all apps. @hide */
@@ -76,8 +75,33 @@
int DEFAULT_SYSTEM = 2;
/** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {CLASSIFIER_TYPE_SELF_PROVIDED, CLASSIFIER_TYPE_DEVICE_DEFAULT,
+ CLASSIFIER_TYPE_ANDROID_DEFAULT})
+ @interface TextClassifierType {
+ }
+ /** Specifies a TextClassifier that runs locally in the app's process. @hide */
+ @FlaggedApi(Flags.FLAG_TEXT_CLASSIFIER_CHOICE_API_ENABLED)
+ @SystemApi
+ int CLASSIFIER_TYPE_SELF_PROVIDED = LOCAL;
+ /**
+ * Specifies a TextClassifier that is set as the default on this particular device. This may be
+ * the same as CLASSIFIER_TYPE_DEVICE_DEFAULT, unless set otherwise by the device manufacturer.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_TEXT_CLASSIFIER_CHOICE_API_ENABLED)
+ @SystemApi
+ int CLASSIFIER_TYPE_DEVICE_DEFAULT = SYSTEM;
+ /** Specifies the TextClassifier that is provided by Android. @hide */
+ @FlaggedApi(Flags.FLAG_TEXT_CLASSIFIER_CHOICE_API_ENABLED)
+ @SystemApi
+ int CLASSIFIER_TYPE_ANDROID_DEFAULT = DEFAULT_SYSTEM;
+
+ /** @hide */
+ @SuppressLint("SwitchIntDef")
static String typeToString(@TextClassifierType int type) {
- switch (type) {
+ int unflaggedType = type;
+ switch (unflaggedType) {
case LOCAL:
return "Local";
case SYSTEM:
@@ -108,6 +132,9 @@
String TYPE_DATE_TIME = "datetime";
/** Flight number in IATA format. */
String TYPE_FLIGHT_NUMBER = "flight";
+ /** Onetime password. */
+ @FlaggedApi(Flags.FLAG_TEXT_CLASSIFIER_CHOICE_API_ENABLED)
+ String TYPE_OTP = "otp";
/**
* Word that users may be interested to look up for meaning.
* @hide
@@ -126,7 +153,8 @@
TYPE_DATE,
TYPE_DATE_TIME,
TYPE_FLIGHT_NUMBER,
- TYPE_DICTIONARY
+ TYPE_DICTIONARY,
+ TYPE_OTP
})
@interface EntityType {}
@@ -198,6 +226,16 @@
String EXTRA_FROM_TEXT_CLASSIFIER = "android.view.textclassifier.extra.FROM_TEXT_CLASSIFIER";
/**
+ * Extra specifying the package name of the app from which the text to be classified originated.
+ *
+ * For example, a notification assistant might use TextClassifier, but the notification
+ * content could originate from a different app. This key allows you to provide
+ * the package name of that source app.
+ */
+ @FlaggedApi(Flags.FLAG_TEXT_CLASSIFIER_CHOICE_API_ENABLED)
+ String EXTRA_TEXT_ORIGIN_PACKAGE = "android.view.textclassifier.extra.TEXT_ORIGIN_PACKAGE";
+
+ /**
* Returns suggested text selection start and end indices, recognized entity types, and their
* associated confidence scores. The entity types are ordered from highest to lowest scoring.
*
diff --git a/core/java/android/webkit/EventLogTags.logtags b/core/java/android/webkit/EventLogTags.logtags
index a90aebd..8bbd5a9 100644
--- a/core/java/android/webkit/EventLogTags.logtags
+++ b/core/java/android/webkit/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package android.webkit;
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 3c854ea..fc3014a 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -16,7 +16,7 @@
package android.widget;
-import static android.view.flags.Flags.enableTouchScrollFeedback;
+import static android.view.flags.Flags.enableScrollFeedbackForTouch;
import static android.view.flags.Flags.scrollFeedbackApi;
import static android.view.flags.Flags.viewVelocityApi;
@@ -3737,7 +3737,7 @@
atEdge = trackMotionScroll(deltaY, incrementalDeltaY);
// TODO: b/360198915 - Add unit testing for using ScrollFeedbackProvider
- if (enableTouchScrollFeedback()) {
+ if (vtev != null && enableScrollFeedbackForTouch()) {
initHapticScrollFeedbackProviderIfNotExists();
mHapticScrollFeedbackProvider.onScrollProgress(
vtev.getDeviceId(), vtev.getSource(), MotionEvent.AXIS_Y,
@@ -3779,7 +3779,7 @@
mTouchMode = TOUCH_MODE_OVERSCROLL;
}
- if (enableTouchScrollFeedback()) {
+ if (vtev != null && enableScrollFeedbackForTouch()) {
initHapticScrollFeedbackProviderIfNotExists();
mHapticScrollFeedbackProvider.onScrollLimit(
vtev.getDeviceId(), vtev.getSource(),
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 2cd3901..9c2833b 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -47,6 +47,7 @@
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager.ServiceCollectionCache;
import android.appwidget.flags.Flags;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
@@ -54,7 +55,6 @@
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentSender;
-import android.content.ServiceConnection;
import android.content.om.FabricatedOverlay;
import android.content.om.OverlayInfo;
import android.content.om.OverlayManager;
@@ -82,7 +82,6 @@
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
-import android.os.IBinder;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
@@ -127,8 +126,8 @@
import com.android.internal.R;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.IRemoteViewsFactory;
-import com.android.internal.widget.remotecompose.core.operations.Theme;
import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.operations.Theme;
import com.android.internal.widget.remotecompose.player.RemoteComposeDocument;
import com.android.internal.widget.remotecompose.player.RemoteComposePlayer;
@@ -1391,8 +1390,10 @@
/**
* @hide
*/
- public CompletableFuture<Void> collectAllIntents(int bitmapSizeLimit) {
- return mCollectionCache.collectAllIntentsNoComplete(this, bitmapSizeLimit);
+ public CompletableFuture<Void> collectAllIntents(int bitmapSizeLimit,
+ @NonNull ServiceCollectionCache collectionCache) {
+ return mCollectionCache.collectAllIntentsNoComplete(this, bitmapSizeLimit,
+ collectionCache);
}
private class RemoteCollectionCache {
@@ -1446,7 +1447,8 @@
}
public @NonNull CompletableFuture<Void> collectAllIntentsNoComplete(
- @NonNull RemoteViews inViews, int bitmapSizeLimit) {
+ @NonNull RemoteViews inViews, int bitmapSizeLimit,
+ @NonNull ServiceCollectionCache collectionCache) {
SparseArray<Intent> idToIntentMapping = new SparseArray<>();
// Collect the number of uinque Intent (which is equal to the number of new connections
// to make) for size allocation and exclude certain collections from being written to
@@ -1478,7 +1480,7 @@
/ numOfIntents;
return connectAllUniqueIntents(individualSize, individualBitmapSizeLimit,
- idToIntentMapping);
+ idToIntentMapping, collectionCache);
}
private void collectAllIntentsInternal(@NonNull RemoteViews inViews,
@@ -1544,13 +1546,14 @@
}
private @NonNull CompletableFuture<Void> connectAllUniqueIntents(int individualSize,
- int individualBitmapSize, @NonNull SparseArray<Intent> idToIntentMapping) {
+ int individualBitmapSize, @NonNull SparseArray<Intent> idToIntentMapping,
+ @NonNull ServiceCollectionCache collectionCache) {
List<CompletableFuture<Void>> intentFutureList = new ArrayList<>();
for (int i = 0; i < idToIntentMapping.size(); i++) {
String currentIntentUri = mIdToUriMapping.get(idToIntentMapping.keyAt(i));
Intent currentIntent = idToIntentMapping.valueAt(i);
intentFutureList.add(getItemsFutureFromIntentWithTimeout(currentIntent,
- individualSize, individualBitmapSize)
+ individualSize, individualBitmapSize, collectionCache)
.thenAccept(items -> {
items.setHierarchyRootData(getHierarchyRootData());
mUriToCollectionMapping.put(currentIntentUri, items);
@@ -1561,7 +1564,8 @@
}
private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout(
- Intent intent, int individualSize, int individualBitmapSize) {
+ Intent intent, int individualSize, int individualBitmapSize,
+ @NonNull ServiceCollectionCache collectionCache) {
if (intent == null) {
Log.e(LOG_TAG, "Null intent received when generating adapter future");
return CompletableFuture.completedFuture(new RemoteCollectionItems
@@ -1581,39 +1585,24 @@
return result;
}
- context.bindService(intent, Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE),
- result.defaultExecutor(), new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName componentName,
- IBinder iBinder) {
- RemoteCollectionItems items;
- try {
- items = IRemoteViewsFactory.Stub.asInterface(iBinder)
- .getRemoteCollectionItems(individualSize,
- individualBitmapSize);
- } catch (RemoteException re) {
- items = new RemoteCollectionItems.Builder().build();
- Log.e(LOG_TAG, "Error getting collection items from the"
- + " factory", re);
- } finally {
- context.unbindService(this);
- }
+ collectionCache.connectAndConsume(intent, iBinder -> {
+ RemoteCollectionItems items;
+ try {
+ items = IRemoteViewsFactory.Stub.asInterface(iBinder)
+ .getRemoteCollectionItems(individualSize,
+ individualBitmapSize);
+ } catch (RemoteException re) {
+ items = new RemoteCollectionItems.Builder().build();
+ Log.e(LOG_TAG, "Error getting collection items from the"
+ + " factory", re);
+ }
- if (items == null) {
- items = new RemoteCollectionItems.Builder().build();
- }
+ if (items == null) {
+ items = new RemoteCollectionItems.Builder().build();
+ }
- result.complete(items);
- }
-
- @Override
- public void onNullBinding(ComponentName name) {
- context.unbindService(this);
- }
-
- @Override
- public void onServiceDisconnected(ComponentName componentName) { }
- });
+ result.complete(items);
+ }, result.defaultExecutor());
result.completeOnTimeout(
new RemoteCollectionItems.Builder().build(),
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 511c832..184933f 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -16,7 +16,7 @@
package android.widget;
-import static android.view.flags.Flags.enableTouchScrollFeedback;
+import static android.view.flags.Flags.enableScrollFeedbackForTouch;
import static android.view.flags.Flags.viewVelocityApi;
import android.annotation.ColorInt;
@@ -909,7 +909,7 @@
}
// TODO: b/360198915 - Add unit tests.
- if (enableTouchScrollFeedback()) {
+ if (enableScrollFeedbackForTouch()) {
if (hitTopLimit || hitBottomLimit) {
initHapticScrollFeedbackProviderIfNotExists();
mHapticScrollFeedbackProvider.onScrollLimit(vtev.getDeviceId(),
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d7750bd..71a832d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.graphics.Paint.NEW_FONT_VARIATION_MANAGEMENT;
import static android.view.ContentInfo.FLAG_CONVERT_TO_PLAIN_TEXT;
import static android.view.ContentInfo.SOURCE_AUTOFILL;
import static android.view.ContentInfo.SOURCE_CLIPBOARD;
@@ -5542,7 +5543,21 @@
&& fontVariationSettings.equals(existingSettings))) {
return true;
}
- boolean effective = mTextPaint.setFontVariationSettings(fontVariationSettings);
+
+ final boolean useFontVariationStore = Flags.typefaceRedesignReadonly()
+ && CompatChanges.isChangeEnabled(NEW_FONT_VARIATION_MANAGEMENT);
+ boolean effective;
+ if (useFontVariationStore) {
+ if (mFontWeightAdjustment != 0
+ && mFontWeightAdjustment != Configuration.FONT_WEIGHT_ADJUSTMENT_UNDEFINED) {
+ mTextPaint.setFontVariationSettings(fontVariationSettings, mFontWeightAdjustment);
+ } else {
+ mTextPaint.setFontVariationSettings(fontVariationSettings);
+ }
+ effective = true;
+ } else {
+ effective = mTextPaint.setFontVariationSettings(fontVariationSettings);
+ }
if (effective && mLayout != null) {
nullLayouts();
@@ -10114,6 +10129,10 @@
outAttrs.extras.putBoolean(
STYLUS_HANDWRITING_ENABLED_ANDROIDX_EXTRAS_KEY, handwritingEnabled);
}
+ if (android.view.inputmethod.Flags.writingTools()) {
+ // default to same behavior as isSuggestionsEnabled().
+ outAttrs.setWritingToolsEnabled(isSuggestionsEnabled());
+ }
ArrayList<Class<? extends HandwritingGesture>> gestures = new ArrayList<>();
gestures.add(SelectGesture.class);
gestures.add(SelectRangeGesture.class);
diff --git a/core/java/android/window/TransitionRequestInfo.java b/core/java/android/window/TransitionRequestInfo.java
index fe936f7..f42c0ec 100644
--- a/core/java/android/window/TransitionRequestInfo.java
+++ b/core/java/android/window/TransitionRequestInfo.java
@@ -44,10 +44,10 @@
private @Nullable ActivityManager.RunningTaskInfo mTriggerTask;
/**
- * If non-null, the task containing the pip activity that participates in this
- * transition.
+ * If non-null, this request might lead to a PiP transition; {@code PipChange} caches both
+ * {@code TaskFragment} token and the {@code TaskInfo} of the task with PiP candidate activity.
*/
- private @Nullable ActivityManager.RunningTaskInfo mPipTask;
+ private @Nullable TransitionRequestInfo.PipChange mPipChange;
/** If non-null, a remote-transition associated with the source of this transition. */
private @Nullable RemoteTransition mRemoteTransition;
@@ -70,7 +70,7 @@
@WindowManager.TransitionType int type,
@Nullable ActivityManager.RunningTaskInfo triggerTask,
@Nullable RemoteTransition remoteTransition) {
- this(type, triggerTask, null /* pipTask */,
+ this(type, triggerTask, null /* pipChange */,
remoteTransition, null /* displayChange */, 0 /* flags */, -1 /* debugId */);
}
@@ -80,7 +80,7 @@
@Nullable ActivityManager.RunningTaskInfo triggerTask,
@Nullable RemoteTransition remoteTransition,
int flags) {
- this(type, triggerTask, null /* pipTask */,
+ this(type, triggerTask, null /* pipChange */,
remoteTransition, null /* displayChange */, flags, -1 /* debugId */);
}
@@ -91,7 +91,7 @@
@Nullable RemoteTransition remoteTransition,
@Nullable TransitionRequestInfo.DisplayChange displayChange,
int flags) {
- this(type, triggerTask, null /* pipTask */, remoteTransition, displayChange, flags,
+ this(type, triggerTask, null /* pipChange */, remoteTransition, displayChange, flags,
-1 /* debugId */);
}
@@ -103,7 +103,9 @@
@Nullable RemoteTransition remoteTransition,
@Nullable TransitionRequestInfo.DisplayChange displayChange,
int flags) {
- this(type, triggerTask, pipTask, remoteTransition, displayChange, flags, -1 /* debugId */);
+ this(type, triggerTask,
+ pipTask != null ? new TransitionRequestInfo.PipChange(pipTask) : null,
+ remoteTransition, displayChange, flags, -1 /* debugId */);
}
/** @hide */
@@ -252,7 +254,7 @@
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
- protected DisplayChange(@android.annotation.NonNull android.os.Parcel in) {
+ /* package-private */ DisplayChange(@android.annotation.NonNull android.os.Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
@@ -289,7 +291,7 @@
};
@DataClass.Generated(
- time = 1697564781403L,
+ time = 1733334462577L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
inputSignatures = "private final int mDisplayId\nprivate @android.annotation.Nullable android.graphics.Rect mStartAbsBounds\nprivate @android.annotation.Nullable android.graphics.Rect mEndAbsBounds\nprivate int mStartRotation\nprivate int mEndRotation\nprivate boolean mPhysicalDisplayChanged\nclass DisplayChange extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genBuilder=false, genConstructor=false)")
@@ -302,6 +304,143 @@
}
+ @DataClass(genToString = true, genSetters = true, genBuilder = false, genConstructor = false)
+ public static final class PipChange implements Parcelable {
+ // In AE case, we might care about the TF token instead of the task token.
+ @android.annotation.NonNull
+ private WindowContainerToken mTaskFragmentToken;
+
+ @android.annotation.NonNull
+ private ActivityManager.RunningTaskInfo mTaskInfo;
+
+ /** Create empty display-change. */
+ public PipChange(ActivityManager.RunningTaskInfo taskInfo) {
+ mTaskFragmentToken = taskInfo.token;
+ mTaskInfo = taskInfo;
+ }
+
+ /** Create a display-change representing a rotation. */
+ public PipChange(WindowContainerToken taskFragmentToken,
+ ActivityManager.RunningTaskInfo taskInfo) {
+ mTaskFragmentToken = taskFragmentToken;
+ mTaskInfo = taskInfo;
+ }
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/window/TransitionRequestInfo.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull WindowContainerToken getTaskFragmentToken() {
+ return mTaskFragmentToken;
+ }
+
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull ActivityManager.RunningTaskInfo getTaskInfo() {
+ return mTaskInfo;
+ }
+
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull PipChange setTaskFragmentToken(@android.annotation.NonNull WindowContainerToken value) {
+ mTaskFragmentToken = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ android.annotation.NonNull.class, null, mTaskFragmentToken);
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull PipChange setTaskInfo(@android.annotation.NonNull ActivityManager.RunningTaskInfo value) {
+ mTaskInfo = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ android.annotation.NonNull.class, null, mTaskInfo);
+ return this;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "PipChange { " +
+ "taskFragmentToken = " + mTaskFragmentToken + ", " +
+ "taskInfo = " + mTaskInfo +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@android.annotation.NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeTypedObject(mTaskFragmentToken, flags);
+ dest.writeTypedObject(mTaskInfo, flags);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ PipChange(@android.annotation.NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ WindowContainerToken taskFragmentToken = (WindowContainerToken) in.readTypedObject(WindowContainerToken.CREATOR);
+ ActivityManager.RunningTaskInfo taskInfo = (ActivityManager.RunningTaskInfo) in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
+
+ this.mTaskFragmentToken = taskFragmentToken;
+ com.android.internal.util.AnnotationValidations.validate(
+ android.annotation.NonNull.class, null, mTaskFragmentToken);
+ this.mTaskInfo = taskInfo;
+ com.android.internal.util.AnnotationValidations.validate(
+ android.annotation.NonNull.class, null, mTaskInfo);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @android.annotation.NonNull Parcelable.Creator<PipChange> CREATOR
+ = new Parcelable.Creator<PipChange>() {
+ @Override
+ public PipChange[] newArray(int size) {
+ return new PipChange[size];
+ }
+
+ @Override
+ public PipChange createFromParcel(@android.annotation.NonNull android.os.Parcel in) {
+ return new PipChange(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1733334462588L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
+ inputSignatures = "private @android.annotation.NonNull android.window.WindowContainerToken mTaskFragmentToken\nprivate @android.annotation.NonNull android.app.ActivityManager.RunningTaskInfo mTaskInfo\nclass PipChange extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genBuilder=false, genConstructor=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+ }
+
@@ -326,9 +465,9 @@
* @param triggerTask
* If non-null, the task containing the activity whose lifecycle change (start or
* finish) has caused this transition to occur.
- * @param pipTask
- * If non-null, the task containing the pip activity that participates in this
- * transition.
+ * @param pipChange
+ * If non-null, this request might lead to a PiP transition; {@code PipChange} caches both
+ * {@code TaskFragment} token and the {@code TaskInfo} of the task with PiP candidate activity.
* @param remoteTransition
* If non-null, a remote-transition associated with the source of this transition.
* @param displayChange
@@ -344,7 +483,7 @@
public TransitionRequestInfo(
@WindowManager.TransitionType int type,
@Nullable ActivityManager.RunningTaskInfo triggerTask,
- @Nullable ActivityManager.RunningTaskInfo pipTask,
+ @Nullable TransitionRequestInfo.PipChange pipChange,
@Nullable RemoteTransition remoteTransition,
@Nullable TransitionRequestInfo.DisplayChange displayChange,
int flags,
@@ -353,7 +492,7 @@
com.android.internal.util.AnnotationValidations.validate(
WindowManager.TransitionType.class, null, mType);
this.mTriggerTask = triggerTask;
- this.mPipTask = pipTask;
+ this.mPipChange = pipChange;
this.mRemoteTransition = remoteTransition;
this.mDisplayChange = displayChange;
this.mFlags = flags;
@@ -380,12 +519,12 @@
}
/**
- * If non-null, the task containing the pip activity that participates in this
- * transition.
+ * If non-null, this request might lead to a PiP transition; {@code PipChange} caches both
+ * {@code TaskFragment} token and the {@code TaskInfo} of the task with PiP candidate activity.
*/
@DataClass.Generated.Member
- public @Nullable ActivityManager.RunningTaskInfo getPipTask() {
- return mPipTask;
+ public @Nullable TransitionRequestInfo.PipChange getPipChange() {
+ return mPipChange;
}
/**
@@ -433,12 +572,12 @@
}
/**
- * If non-null, the task containing the pip activity that participates in this
- * transition.
+ * If non-null, this request might lead to a PiP transition; {@code PipChange} caches both
+ * {@code TaskFragment} token and the {@code TaskInfo} of the task with PiP candidate activity.
*/
@DataClass.Generated.Member
- public @android.annotation.NonNull TransitionRequestInfo setPipTask(@android.annotation.NonNull ActivityManager.RunningTaskInfo value) {
- mPipTask = value;
+ public @android.annotation.NonNull TransitionRequestInfo setPipChange(@android.annotation.NonNull TransitionRequestInfo.PipChange value) {
+ mPipChange = value;
return this;
}
@@ -471,10 +610,10 @@
return "TransitionRequestInfo { " +
"type = " + typeToString() + ", " +
"triggerTask = " + mTriggerTask + ", " +
- "pipTask = " + mPipTask + ", " +
+ "pipChange = " + mPipChange + ", " +
"remoteTransition = " + mRemoteTransition + ", " +
"displayChange = " + mDisplayChange + ", " +
- "flags = " + Integer.toHexString(mFlags) + ", " +
+ "flags = " + mFlags + ", " +
"debugId = " + mDebugId +
" }";
}
@@ -487,13 +626,13 @@
byte flg = 0;
if (mTriggerTask != null) flg |= 0x2;
- if (mPipTask != null) flg |= 0x4;
+ if (mPipChange != null) flg |= 0x4;
if (mRemoteTransition != null) flg |= 0x8;
if (mDisplayChange != null) flg |= 0x10;
dest.writeByte(flg);
dest.writeInt(mType);
if (mTriggerTask != null) dest.writeTypedObject(mTriggerTask, flags);
- if (mPipTask != null) dest.writeTypedObject(mPipTask, flags);
+ if (mPipChange != null) dest.writeTypedObject(mPipChange, flags);
if (mRemoteTransition != null) dest.writeTypedObject(mRemoteTransition, flags);
if (mDisplayChange != null) dest.writeTypedObject(mDisplayChange, flags);
dest.writeInt(mFlags);
@@ -514,7 +653,7 @@
byte flg = in.readByte();
int type = in.readInt();
ActivityManager.RunningTaskInfo triggerTask = (flg & 0x2) == 0 ? null : (ActivityManager.RunningTaskInfo) in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
- ActivityManager.RunningTaskInfo pipTask = (flg & 0x4) == 0 ? null : (ActivityManager.RunningTaskInfo) in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
+ TransitionRequestInfo.PipChange pipChange = (flg & 0x4) == 0 ? null : (TransitionRequestInfo.PipChange) in.readTypedObject(TransitionRequestInfo.PipChange.CREATOR);
RemoteTransition remoteTransition = (flg & 0x8) == 0 ? null : (RemoteTransition) in.readTypedObject(RemoteTransition.CREATOR);
TransitionRequestInfo.DisplayChange displayChange = (flg & 0x10) == 0 ? null : (TransitionRequestInfo.DisplayChange) in.readTypedObject(TransitionRequestInfo.DisplayChange.CREATOR);
int flags = in.readInt();
@@ -524,7 +663,7 @@
com.android.internal.util.AnnotationValidations.validate(
WindowManager.TransitionType.class, null, mType);
this.mTriggerTask = triggerTask;
- this.mPipTask = pipTask;
+ this.mPipChange = pipChange;
this.mRemoteTransition = remoteTransition;
this.mDisplayChange = displayChange;
this.mFlags = flags;
@@ -548,10 +687,10 @@
};
@DataClass.Generated(
- time = 1697564781438L,
+ time = 1733334462604L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
- inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mPipTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\nprivate final int mDebugId\n java.lang.String typeToString()\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
+ inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.PipChange mPipChange\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\nprivate final int mDebugId\n java.lang.String typeToString()\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/window/WindowContext.java b/core/java/android/window/WindowContext.java
index 2b370b9..84a8b8f 100644
--- a/core/java/android/window/WindowContext.java
+++ b/core/java/android/window/WindowContext.java
@@ -32,6 +32,7 @@
import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
import java.lang.ref.Reference;
@@ -95,20 +96,20 @@
}
/**
- * Updates this context to a new displayId.
+ * Moves this context to another display.
* <p>
- * Note that this doesn't re-parent previously attached windows (they should be removed and
- * re-added manually after this is called). Resources associated with this context will have
- * the correct value and configuration for the new display after this is called.
+ * Note that this re-parents all the previously attached windows. Resources associated with this
+ * context will have the correct value and configuration for the new display after this is
+ * called.
*/
- @Override
- public void updateDisplay(int displayId) {
- if (displayId == getDisplayId()) {
- return;
+ public void reparentToDisplay(int displayId) {
+ if (Flags.reparentWindowTokenApi()) {
+ if (displayId == getDisplayId()) {
+ return;
+ }
+ super.updateDisplay(displayId);
+ mController.reparentToDisplayArea(mType, displayId, mOptions);
}
- super.updateDisplay(displayId);
- mController.detachIfNeeded();
- mController.attachToDisplayArea(mType, displayId, mOptions);
}
@Override
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index c9ac245..1e2f454 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -158,6 +158,21 @@
}
}
+ /**
+ * Reparents the window context from the current attached display to another. {@code type} and
+ * {@code options} must be the same as the previous attach call, otherwise this will fail
+ * silently.
+ */
+ public void reparentToDisplayArea(
+ @WindowType int type, int displayId, @Nullable Bundle options) {
+ if (mAttachedToDisplayArea != AttachStatus.STATUS_ATTACHED) {
+ attachToDisplayArea(type, displayId, options);
+ return;
+ }
+ // No need to propagate type and options as this is already attached and they can't change.
+ getWindowTokenClientController().reparentToDisplayArea(mToken, displayId);
+ }
+
/** Gets the {@link WindowTokenClientController}. */
@VisibleForTesting
@NonNull
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 6e76d8d..a551fe7 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -166,7 +166,7 @@
@VisibleForTesting
public void onConfigurationChangedInner(@NonNull Context context,
@NonNull Configuration newConfig, int newDisplayId, boolean shouldReportConfigChange) {
- CompatibilityInfo.applyOverrideScaleIfNeeded(newConfig);
+ CompatibilityInfo.applyOverrideIfNeeded(newConfig);
final boolean displayChanged;
final boolean shouldUpdateResources;
final int diff;
diff --git a/core/java/android/window/WindowTokenClientController.java b/core/java/android/window/WindowTokenClientController.java
index fa34595..1ec05b6 100644
--- a/core/java/android/window/WindowTokenClientController.java
+++ b/core/java/android/window/WindowTokenClientController.java
@@ -197,6 +197,21 @@
}
}
+ /**
+ * Reparents a {@link WindowTokenClient} and its associated WindowContainer if there's one.
+ */
+ public void reparentToDisplayArea(@NonNull WindowTokenClient client, int displayId) {
+ try {
+ if (!getWindowManagerService().reparentWindowContextToDisplayArea(mAppThread, client,
+ displayId)) {
+ Log.e(TAG,
+ "Didn't succeed reparenting of " + client + " to displayId=" + displayId);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private void onWindowContextTokenAttached(@NonNull WindowTokenClient client,
@NonNull WindowContextInfo info, boolean shouldReportConfigChange) {
recordWindowContextToken(client);
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 4fb5fa7..a04071a 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -449,6 +449,14 @@
}
flag {
+ name: "reparent_window_token_api"
+ namespace: "lse_desktop_experience"
+ description: "Allows to reparent a window token to a different display"
+ is_fixed_read_only: true
+ bug: "381258683"
+}
+
+flag {
name: "enable_desktop_windowing_hsum"
namespace: "lse_desktop_experience"
description: "Enables HSUM on desktop mode."
@@ -460,4 +468,25 @@
namespace: "lse_desktop_experience"
description: "Enable multiple desktop sessions for desktop windowing."
bug: "379158791"
+}
+
+flag {
+ name: "enable_connected_displays_dnd"
+ namespace: "lse_desktop_experience"
+ description: "Enable drag-and-drop between connected displays."
+ bug: "381793841"
+}
+
+flag {
+ name: "enable_connected_displays_window_drag"
+ namespace: "lse_desktop_experience"
+ description: "Enable window drag between connected displays."
+ bug: "381172172"
+}
+
+flag {
+ name: "enable_bug_fixes_for_secondary_display"
+ namespace: "lse_desktop_experience"
+ description: "Bugfixes / papercuts to bring Desktop Windowing to secondary displays."
+ bug: "382023296"
}
\ No newline at end of file
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 0b034b6..30f0c73 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -453,3 +453,11 @@
is_fixed_read_only: true
bug: "376407910"
}
+
+flag {
+ name: "relative_insets"
+ namespace: "windowing_frontend"
+ description: "Support insets definition and calculation relative to task bounds."
+ bug: "277292497"
+ is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 92f9e60..5d4c408 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -267,7 +267,9 @@
return Flags.useWearMaterial3Ui()
&& CompatChanges.isChangeEnabled(WEAR_MATERIAL3_ALERTDIALOG)
&& context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)
- && context.getThemeResId() == com.android.internal.R.style.Theme_DeviceDefault;
+ && (context.getThemeResId() == com.android.internal.R.style.Theme_DeviceDefault
+ || context.getThemeResId()
+ == com.android.internal.R.style.Theme_DeviceDefault_Dialog_Alert);
}
static boolean canTextInput(View v) {
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index f01aa80..2cfc680 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -163,5 +163,4 @@
void finishOperationForDevice(IBinder clientId, int code, int uid, String packageName,
@nullable String attributionTag, int virtualDeviceId);
List<AppOpsManager.PackageOps> getPackagesForOpsForDevice(in int[] ops, String persistentDeviceId);
- oneway void noteOperationsInBatch(in Map batchedNoteOps);
}
diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
index 429a6a2..592ea9e 100644
--- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
+++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
@@ -91,6 +91,7 @@
SoftInputShowHideReason.CONTROL_WINDOW_INSETS_ANIMATION,
SoftInputShowHideReason.SHOW_INPUT_TARGET_CHANGED,
SoftInputShowHideReason.HIDE_INPUT_TARGET_CHANGED,
+ SoftInputShowHideReason.REASON_HIDE_WINDOW_LOST_FOCUS,
})
public @interface SoftInputShowHideReason {
/** Default, undefined reason. */
@@ -418,4 +419,7 @@
* {@link android.view.InsetsController#controlWindowInsetsAnimation}.
*/
int CONTROL_WINDOW_INSETS_ANIMATION = ImeProtoEnums.REASON_CONTROL_WINDOW_INSETS_ANIMATION;
+
+ /** Hide soft input when the window lost focus. */
+ int REASON_HIDE_WINDOW_LOST_FOCUS = ImeProtoEnums.REASON_HIDE_WINDOW_LOST_FOCUS;
}
diff --git a/core/java/com/android/internal/jank/EventLogTags.logtags b/core/java/com/android/internal/jank/EventLogTags.logtags
index 66ee131..dfec499 100644
--- a/core/java/com/android/internal/jank/EventLogTags.logtags
+++ b/core/java/com/android/internal/jank/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package com.android.internal.jank;
diff --git a/core/java/com/android/internal/os/TEST_MAPPING b/core/java/com/android/internal/os/TEST_MAPPING
index 4400ed1..1923c5f 100644
--- a/core/java/com/android/internal/os/TEST_MAPPING
+++ b/core/java/com/android/internal/os/TEST_MAPPING
@@ -20,7 +20,7 @@
"file_patterns": [
"BinderDeathDispatcher\\.java"
],
- "name": "FrameworksCoreTests_internal_os_binder"
+ "name": "FrameworksCoreTests_all_binder"
},
{
"file_patterns": [
diff --git a/core/java/com/android/internal/policy/KeyInterceptionInfo.java b/core/java/com/android/internal/policy/KeyInterceptionInfo.java
index b20f6d2..fed8fe3 100644
--- a/core/java/com/android/internal/policy/KeyInterceptionInfo.java
+++ b/core/java/com/android/internal/policy/KeyInterceptionInfo.java
@@ -27,11 +27,13 @@
// Debug friendly name to help identify the window
public final String windowTitle;
public final int windowOwnerUid;
+ public final int inputFeaturesFlags;
- public KeyInterceptionInfo(int type, int flags, String title, int uid) {
+ public KeyInterceptionInfo(int type, int flags, String title, int uid, int inputFeaturesFlags) {
layoutParamsType = type;
layoutParamsPrivateFlags = flags;
windowTitle = title;
windowOwnerUid = uid;
+ this.inputFeaturesFlags = inputFeaturesFlags;
}
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index ff08dd2..3e2f301 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -67,6 +67,13 @@
// ---- Methods below are for use by the status bar policy services ----
// You need the STATUS_BAR_SERVICE permission
RegisterStatusBarResult registerStatusBar(IStatusBar callbacks);
+ /**
+ * Registers the status bar for all displays.
+ *
+ * Returns a map of all display IDs (as strings) to their corresponding RegisterStatusBarResult
+ * objects.
+ */
+ Map<String, RegisterStatusBarResult> registerStatusBarForAllDisplays(IStatusBar callbacks);
void onPanelRevealed(boolean clearNotificationEffects, int numItems);
void onPanelHidden();
// Mark current notifications as "seen" and stop ringing, vibrating, blinking.
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 1e965c5d..bda7547 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
+import android.ravenwood.annotation.RavenwoodReplace;
import android.util.ArraySet;
import android.util.EmptyArray;
@@ -39,6 +40,10 @@
/**
* Static utility methods for arrays that aren't already included in {@link java.util.Arrays}.
+ * <p>
+ * Test with:
+ * <code>atest FrameworksUtilTests:com.android.internal.util.ArrayUtilsTest</code>
+ * <code>atest FrameworksUtilTestsRavenwood:com.android.internal.util.ArrayUtilsTest</code>
*/
@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ArrayUtils {
@@ -85,6 +90,69 @@
}
/**
+ * This is like <code>new byte[length]</code>, but it allocates the array as non-movable. This
+ * prevents copies of the data from being left on the Java heap as a result of heap compaction.
+ * Use this when the array will contain sensitive data such as a password or cryptographic key
+ * that needs to be wiped from memory when no longer needed. The owner of the array is still
+ * responsible for the zeroization; {@link #zeroize(byte[])} should be used to do so.
+ *
+ * @param length the length of the array to allocate
+ * @return the new array
+ */
+ public static byte[] newNonMovableByteArray(int length) {
+ return (byte[]) VMRuntime.getRuntime().newNonMovableArray(byte.class, length);
+ }
+
+ /**
+ * Like {@link #newNonMovableByteArray(int)}, but allocates a char array.
+ *
+ * @param length the length of the array to allocate
+ * @return the new array
+ */
+ public static char[] newNonMovableCharArray(int length) {
+ return (char[]) VMRuntime.getRuntime().newNonMovableArray(char.class, length);
+ }
+
+ /**
+ * Zeroizes a byte array as securely as possible. Use this when the array contains sensitive
+ * data such as a password or cryptographic key.
+ * <p>
+ * This zeroizes the array in a way that is guaranteed to not be optimized out by the compiler.
+ * If supported by the architecture, it zeroizes the data not just in the L1 data cache but also
+ * in other levels of the memory hierarchy up to and including main memory (but not above that).
+ * <p>
+ * This works on any <code>byte[]</code>, but to ensure that copies of the array aren't left on
+ * the Java heap the array should have been allocated with {@link #newNonMovableByteArray(int)}.
+ * Use on other arrays might also introduce performance anomalies.
+ *
+ * @param array the array to zeroize. If null, this method has no effect.
+ */
+ @RavenwoodReplace public static native void zeroize(byte[] array);
+
+ /**
+ * Replacement of the above method for host-side unit testing that doesn't support JNI yet.
+ */
+ public static void zeroize$ravenwood(byte[] array) {
+ if (array != null) {
+ Arrays.fill(array, (byte) 0);
+ }
+ }
+
+ /**
+ * Like {@link #zeroize(byte[])}, but for char arrays.
+ */
+ @RavenwoodReplace public static native void zeroize(char[] array);
+
+ /**
+ * Replacement of the above method for host-side unit testing that doesn't support JNI yet.
+ */
+ public static void zeroize$ravenwood(char[] array) {
+ if (array != null) {
+ Arrays.fill(array, (char) 0);
+ }
+ }
+
+ /**
* Checks if the beginnings of two byte arrays are equal.
*
* @param array1 the first byte array
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 754f77e7..d49afa7 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -435,9 +435,11 @@
public void startListeningForLatencyTrackerConfigChanges() {
final Context context = ActivityThread.currentApplication();
if (context == null) {
- if (DEBUG) {
- Log.d(TAG, "No application for package: " + ActivityThread.currentPackageName());
- }
+ Log.e(
+ TAG,
+ String.format(
+ "No application for package: %s. Latency Tracker Disabled",
+ ActivityThread.currentPackageName()));
return;
}
if (context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG) != PERMISSION_GRANTED) {
diff --git a/core/java/com/android/internal/view/ScrollCaptureInternal.java b/core/java/com/android/internal/view/ScrollCaptureInternal.java
index 72b5488..0ed0613 100644
--- a/core/java/com/android/internal/view/ScrollCaptureInternal.java
+++ b/core/java/com/android/internal/view/ScrollCaptureInternal.java
@@ -16,6 +16,8 @@
package com.android.internal.view;
+import static android.view.flags.Flags.scrollCaptureRelaxScrollViewCriteria;
+
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
@@ -49,7 +51,7 @@
public static final int TYPE_FIXED = 0;
/**
- * Slides a single child view using mScrollX/mScrollY.
+ * Moves the viewport across absolute positioned child views using the scrollY property.
*/
public static final int TYPE_SCROLLING = 1;
@@ -63,7 +65,7 @@
/**
* Unknown scrollable view with no child views (or not a subclass of ViewGroup).
*/
- private static final int TYPE_OPAQUE = 3;
+ public static final int TYPE_OPAQUE = 3;
/**
* Performs tests on the given View and determines:
@@ -73,7 +75,7 @@
* This needs to be fast and not alloc memory. It's called on everything in the tree not marked
* as excluded during scroll capture search.
*/
- private static int detectScrollingType(View view) {
+ public static int detectScrollingType(View view) {
// Confirm that it can scroll.
if (!(view.canScrollVertically(DOWN) || view.canScrollVertically(UP))) {
// Nothing to scroll here, move along.
@@ -95,25 +97,25 @@
if (DEBUG_VERBOSE) {
Log.v(TAG, "hint: is a subclass of ViewGroup");
}
-
- // ScrollViews accept only a single child.
- if (((ViewGroup) view).getChildCount() > 1) {
- if (DEBUG_VERBOSE) {
- Log.v(TAG, "hint: scrollable with multiple children");
+ // Flag: Optionally allow ScrollView-like ViewGroups which have more than one child view.
+ if (!scrollCaptureRelaxScrollViewCriteria()) {
+ // ScrollViews accept only a single child.
+ if (((ViewGroup) view).getChildCount() > 1) {
+ if (DEBUG_VERBOSE) {
+ Log.v(TAG, "hint: scrollable with multiple children");
+ }
+ return TYPE_RECYCLING;
}
- return TYPE_RECYCLING;
}
// At least one child view is required.
- if (((ViewGroup) view).getChildCount() < 1) {
- if (DEBUG_VERBOSE) {
- Log.v(TAG, "scrollable with no children");
- }
+ if (((ViewGroup) view).getChildCount() == 0) {
+ Log.w(TAG, "scrollable but no children!");
return TYPE_OPAQUE;
}
if (DEBUG_VERBOSE) {
Log.v(TAG, "hint: single child view");
}
- //Because recycling containers don't use scrollY, a non-zero value means Scroll view.
+ // Because recycling containers don't use scrollY, a non-zero value means Scroll view.
if (view.getScrollY() != 0) {
if (DEBUG_VERBOSE) {
Log.v(TAG, "hint: scrollY != 0");
@@ -132,7 +134,7 @@
Log.v(TAG, "hint: cannot be scrolled up");
}
- // canScrollVertically(UP) == false, getScrollY() == 0, getChildCount() == 1.
+ // canScrollVertically(UP) == false, getScrollY() == 0, getChildCount() >= 1.
// For Recycling containers, this should be a no-op (RecyclerView logs a warning)
view.scrollTo(view.getScrollX(), 1);
diff --git a/core/java/com/android/internal/widget/flags.aconfig b/core/java/com/android/internal/widget/flags.aconfig
new file mode 100644
index 0000000..f05aa4f
--- /dev/null
+++ b/core/java/com/android/internal/widget/flags.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.internal.widget.flags"
+container: "system"
+
+flag {
+ name: "hide_last_char_with_physical_input"
+ namespace: "input"
+ description: "Feature flag for changing the default of hiding the last interacted symbol when a physical input device is present"
+ bug: "339270220"
+}
diff --git a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
index 38685b6..5177a03 100644
--- a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
+++ b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
@@ -435,10 +435,15 @@
private void refreshCoordinatesAndOverflowDirection(Rect contentRectOnScreen) {
refreshViewPort();
- // Initialize x ensuring that the toolbar isn't rendered behind the nav bar in
- // landscape.
- final int x = Math.clamp(contentRectOnScreen.centerX() - mPopupWindow.getWidth() / 2,
- mViewPortOnScreen.left, mViewPortOnScreen.right - mPopupWindow.getWidth());
+ final int x;
+ if (mPopupWindow.getWidth() > mViewPortOnScreen.width()) {
+ // Not enough space - prefer to position as far left as possible
+ x = mViewPortOnScreen.left;
+ } else {
+ // Initialize x ensuring that the toolbar isn't rendered behind the system bar insets
+ x = Math.clamp(contentRectOnScreen.centerX() - mPopupWindow.getWidth() / 2,
+ mViewPortOnScreen.left, mViewPortOnScreen.right - mPopupWindow.getWidth());
+ }
final int y;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
index f142886..33f93fc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -24,6 +24,8 @@
import com.android.internal.widget.remotecompose.core.operations.IntegerExpression;
import com.android.internal.widget.remotecompose.core.operations.NamedVariable;
import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
+import com.android.internal.widget.remotecompose.core.operations.ShaderData;
+import com.android.internal.widget.remotecompose.core.operations.TextData;
import com.android.internal.widget.remotecompose.core.operations.Theme;
import com.android.internal.widget.remotecompose.core.operations.layout.ClickModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
@@ -45,6 +47,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
@@ -470,6 +473,21 @@
float mBottom;
@Nullable final String mMetadata;
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ClickAreaRepresentation)) return false;
+ ClickAreaRepresentation that = (ClickAreaRepresentation) o;
+ return mId == that.mId
+ && Objects.equals(mContentDescription, that.mContentDescription)
+ && Objects.equals(mMetadata, that.mMetadata);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mId, mContentDescription, mMetadata);
+ }
+
public ClickAreaRepresentation(
int id,
@Nullable String contentDescription,
@@ -679,7 +697,9 @@
}
}
}
+ op.markNotDirty();
op.apply(context);
+ context.incrementOpCount();
}
}
@@ -754,9 +774,13 @@
float right,
float bottom,
@Nullable String metadata) {
- mClickAreas.add(
+
+ ClickAreaRepresentation car =
new ClickAreaRepresentation(
- id, contentDescription, left, top, right, bottom, metadata));
+ id, contentDescription, left, top, right, bottom, metadata);
+
+ boolean old = mClickAreas.remove(car);
+ mClickAreas.add(car);
}
/**
@@ -977,6 +1001,16 @@
private final float[] mScaleOutput = new float[2];
private final float[] mTranslateOutput = new float[2];
private int mRepaintNext = -1; // delay to next repaint -1 = don't 1 = asap
+ private int mLastOpCount;
+
+ /**
+ * This is the number of ops used to calculate the last frame.
+ *
+ * @return number of ops
+ */
+ public int getOpsPerFrame() {
+ return mLastOpCount;
+ }
/**
* Returns > 0 if it needs to repaint
@@ -994,6 +1028,7 @@
* @param theme the theme we want to use for this document.
*/
public void paint(@NonNull RemoteContext context, int theme) {
+ context.getLastOpCount();
context.getPaintContext().clearNeedsRepaint();
context.loadFloat(RemoteContext.ID_DENSITY, context.getDensity());
context.mMode = RemoteContext.ContextMode.UNSET;
@@ -1004,21 +1039,24 @@
context.mRemoteComposeState = mRemoteComposeState;
context.mRemoteComposeState.setContext(context);
+ // If we have a content sizing set, we are going to take the original document
+ // dimension into account and apply scale+translate according to the RootContentBehavior
+ // rules.
if (mContentSizing == RootContentBehavior.SIZING_SCALE) {
// we need to add canvas transforms ops here
computeScale(context.mWidth, context.mHeight, mScaleOutput);
- computeTranslate(
- context.mWidth,
- context.mHeight,
- mScaleOutput[0],
- mScaleOutput[1],
- mTranslateOutput);
+ float sw = mScaleOutput[0];
+ float sh = mScaleOutput[1];
+ computeTranslate(context.mWidth, context.mHeight, sw, sh, mTranslateOutput);
context.mPaintContext.translate(mTranslateOutput[0], mTranslateOutput[1]);
- context.mPaintContext.scale(mScaleOutput[0], mScaleOutput[1]);
+ context.mPaintContext.scale(sw, sh);
+ } else {
+ // If not, we set the document width and height to be the current context width and
+ // height.
+ setWidth((int) context.mWidth);
+ setHeight((int) context.mHeight);
}
mTimeVariables.updateTime(context);
- context.loadFloat(RemoteContext.ID_WINDOW_WIDTH, context.mWidth);
- context.loadFloat(RemoteContext.ID_WINDOW_HEIGHT, context.mHeight);
mRepaintNext = context.updateOps();
if (mRootLayoutComponent != null) {
if (context.mWidth != mRootLayoutComponent.getWidth()
@@ -1028,11 +1066,11 @@
if (mRootLayoutComponent.needsMeasure()) {
mRootLayoutComponent.layout(context);
}
- // TODO -- this should be specifically about applying animation, not paint
- mRootLayoutComponent.paint(context.getPaintContext());
- context.mPaintContext.reset();
- // TODO -- should be able to remove this
- mRootLayoutComponent.updateVariables(context);
+ if (mRootLayoutComponent.needsBoundsAnimation()) {
+ mRepaintNext = 1;
+ mRootLayoutComponent.clearNeedsBoundsAnimation();
+ mRootLayoutComponent.animatingBounds(context);
+ }
if (DEBUG) {
String hierarchy = mRootLayoutComponent.displayHierarchy();
System.out.println(hierarchy);
@@ -1058,6 +1096,7 @@
op.markNotDirty();
((VariableSupport) op).updateVariables(context);
}
+ context.incrementOpCount();
op.apply(context);
}
}
@@ -1067,10 +1106,41 @@
mRepaintNext = 1;
}
context.mMode = RemoteContext.ContextMode.UNSET;
- // System.out.println(">> " + ( System.nanoTime() - time)*1E-6f+" ms");
if (DEBUG && mRootLayoutComponent != null) {
System.out.println(mRootLayoutComponent.displayHierarchy());
}
+ mLastOpCount = context.getLastOpCount();
+ }
+
+ /**
+ * Get an estimated number of operations executed in a paint
+ *
+ * @return number of operations
+ */
+ public int getNumberOfOps() {
+ int count = mOperations.size();
+
+ for (Operation mOperation : mOperations) {
+ if (mOperation instanceof Component) {
+ count += getChildOps((Component) mOperation);
+ }
+ }
+ return count;
+ }
+
+ private int getChildOps(@NonNull Component base) {
+ int count = base.mList.size();
+ for (Operation mOperation : base.mList) {
+
+ if (mOperation instanceof Component) {
+ int mult = 1;
+ if (mOperation instanceof LoopOperation) {
+ mult = ((LoopOperation) mOperation).estimateIterations();
+ }
+ count += mult * getChildOps((Component) mOperation);
+ }
+ }
+ return count;
}
@NonNull
@@ -1094,6 +1164,9 @@
if (mOperation instanceof Component) {
Component com = (Component) mOperation;
count += addChildren(com, map, buffer);
+ } else if (mOperation instanceof LoopOperation) {
+ LoopOperation com = (LoopOperation) mOperation;
+ count += addChildren(com, map, buffer);
}
}
@@ -1131,6 +1204,35 @@
if (mOperation instanceof Component) {
count += addChildren((Component) mOperation, map, tmp);
}
+ if (mOperation instanceof LoopOperation) {
+ count += addChildren((LoopOperation) mOperation, map, tmp);
+ }
+ }
+ return count;
+ }
+
+ private int addChildren(
+ @NonNull LoopOperation base,
+ @NonNull HashMap<String, int[]> map,
+ @NonNull WireBuffer tmp) {
+ int count = base.mList.size();
+ for (Operation mOperation : base.mList) {
+ Class<? extends Operation> c = mOperation.getClass();
+ int[] values;
+ if (map.containsKey(c.getSimpleName())) {
+ values = map.get(c.getSimpleName());
+ } else {
+ values = new int[2];
+ map.put(c.getSimpleName(), values);
+ }
+ values[0] += 1;
+ values[1] += sizeOfComponent(mOperation, tmp);
+ if (mOperation instanceof Component) {
+ count += addChildren((Component) mOperation, map, tmp);
+ }
+ if (mOperation instanceof LoopOperation) {
+ count += addChildren((LoopOperation) mOperation, map, tmp);
+ }
}
return count;
}
@@ -1163,4 +1265,29 @@
public List<Operation> getOperations() {
return mOperations;
}
+
+ /** defines if a shader can be run */
+ public interface ShaderControl {
+ boolean isShaderValid(String shader);
+ }
+
+ /**
+ * validate the shaders
+ *
+ * @param context the remote context
+ * @param ctl the call back to allow evaluation of shaders
+ */
+ public void checkShaders(RemoteContext context, ShaderControl ctl) {
+ for (Operation op : mOperations) {
+ if (op instanceof TextData) {
+ op.apply(context);
+ }
+ if (op instanceof ShaderData) {
+ ShaderData sd = (ShaderData) op;
+ int id = sd.getShaderTextId();
+ String str = context.getText(id);
+ sd.enable(ctl.isShaderValid(str));
+ }
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
index 11e58ba..5c3df7e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
@@ -333,6 +333,7 @@
public void overrideColor(int id, int color) {
mColorOverride[id] = true;
mColorMap.put(id, color);
+ updateListeners(id);
}
/** Clear the color Overrides */
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
index 003acb7..b5587ce 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -40,6 +40,7 @@
* <p>We also contain a PaintContext, so that any operation can draw as needed.
*/
public abstract class RemoteContext {
+ private static final int MAX_OP_COUNT = 100_000; // Maximum cmds per frame
protected @NonNull CoreDocument mDocument =
new CoreDocument(); // todo: is this a valid way to initialize? bbade@
public @NonNull RemoteComposeState mRemoteComposeState =
@@ -52,6 +53,7 @@
int mDebug = 0;
+ private int mOpCount;
private int mTheme = Theme.UNSPECIFIED;
public float mWidth = 0f;
@@ -493,6 +495,9 @@
public static final int ID_DENSITY = 27;
+ /** Defines when the last build was made */
+ public static final int ID_API_LEVEL = 28;
+
public static final float FLOAT_DENSITY = Utils.asNan(ID_DENSITY);
/** CONTINUOUS_SEC is seconds from midnight looping every hour 0-3600 */
@@ -566,6 +571,9 @@
/** Ambient light level in SI lux */
public static final float FLOAT_LIGHT = Utils.asNan(ID_LIGHT);
+ /** When was this player built */
+ public static final float FLOAT_API_LEVEL = Utils.asNan(ID_API_LEVEL);
+
///////////////////////////////////////////////////////////////////////////////////////////////
// Click handling
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -625,4 +633,23 @@
float right,
float bottom,
int metadataId);
+
+ /** increments the count of operations executed in a pass */
+ public void incrementOpCount() {
+ mOpCount++;
+ if (mOpCount > MAX_OP_COUNT) {
+ throw new RuntimeException("Too many operations executed");
+ }
+ }
+
+ /**
+ * Get the last Op Count and clear the count.
+ *
+ * @return the number of ops executed.
+ */
+ public int getLastOpCount() {
+ int count = mOpCount;
+ mOpCount = 0;
+ return count;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
index 0ed6005..ea917db 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
@@ -24,6 +24,8 @@
/** This generates the standard system variables for time. */
public class TimeVariables {
+ private static final float BUILD = 0.02f;
+
/**
* This class populates all time variables in the system
*
@@ -57,6 +59,7 @@
context.loadFloat(RemoteContext.ID_CALENDAR_MONTH, month);
context.loadFloat(RemoteContext.ID_DAY_OF_MONTH, month);
context.loadFloat(RemoteContext.ID_WEEK_DAY, day_week);
+ context.loadFloat(RemoteContext.ID_API_LEVEL, CoreDocument.getDocumentApiLevel() + BUILD);
}
/**
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
index efd31af..bb112d1 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
@@ -21,6 +21,7 @@
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteComposeOperation;
import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
@@ -29,7 +30,8 @@
import java.util.List;
/** Add a click area to the document */
-public class ClickArea extends Operation implements RemoteComposeOperation, AccessibleComponent {
+public class ClickArea extends Operation
+ implements RemoteComposeOperation, AccessibleComponent, VariableSupport {
private static final int OP_CODE = Operations.CLICK_AREA;
private static final String CLASS_NAME = "ClickArea";
int mId;
@@ -38,6 +40,10 @@
float mTop;
float mRight;
float mBottom;
+ float mOutLeft;
+ float mOutTop;
+ float mOutRight;
+ float mOutBottom;
int mMetadata;
/**
@@ -62,11 +68,35 @@
int metadata) {
this.mId = id;
this.mContentDescription = contentDescription;
- this.mLeft = left;
- this.mTop = top;
- this.mRight = right;
- this.mBottom = bottom;
- this.mMetadata = metadata;
+ mOutLeft = mLeft = left;
+ mOutTop = mTop = top;
+ mOutRight = mRight = right;
+ mOutBottom = mBottom = bottom;
+ mMetadata = metadata;
+ }
+
+ @Override
+ public void registerListening(@NonNull RemoteContext context) {
+ if (Float.isNaN(mLeft)) {
+ context.listensTo(Utils.idFromNan(mLeft), this);
+ }
+ if (Float.isNaN(mTop)) {
+ context.listensTo(Utils.idFromNan(mTop), this);
+ }
+ if (Float.isNaN(mRight)) {
+ context.listensTo(Utils.idFromNan(mRight), this);
+ }
+ if (Float.isNaN(mBottom)) {
+ context.listensTo(Utils.idFromNan(mBottom), this);
+ }
+ }
+
+ @Override
+ public void updateVariables(@NonNull RemoteContext context) {
+ mOutLeft = Float.isNaN(mLeft) ? context.getFloat(Utils.idFromNan(mLeft)) : mLeft;
+ mOutTop = Float.isNaN(mTop) ? context.getFloat(Utils.idFromNan(mTop)) : mTop;
+ mRight = Float.isNaN(mRight) ? context.getFloat(Utils.idFromNan(mRight)) : mRight;
+ mOutBottom = Float.isNaN(mBottom) ? context.getFloat(Utils.idFromNan(mBottom)) : mBottom;
}
@Override
@@ -102,10 +132,8 @@
@Override
public void apply(@NonNull RemoteContext context) {
- if (context.getMode() != RemoteContext.ContextMode.DATA) {
- return;
- }
- context.addClickArea(mId, mContentDescription, mLeft, mTop, mRight, mBottom, mMetadata);
+ context.addClickArea(
+ mId, mContentDescription, mOutLeft, mOutTop, mOutRight, mOutBottom, mMetadata);
}
@NonNull
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
index 8e4098e..891367e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
@@ -50,6 +50,7 @@
@Nullable HashMap<String, float[]> mUniformFloatMap = null;
@Nullable HashMap<String, int[]> mUniformIntMap;
@Nullable HashMap<String, Integer> mUniformBitmapMap = null;
+ private boolean mShaderValid = false;
public ShaderData(
int shaderID,
@@ -358,7 +359,9 @@
@Override
public void apply(@NonNull RemoteContext context) {
- context.loadShader(mShaderID, this);
+ if (mShaderValid) {
+ context.loadShader(mShaderID, this);
+ }
}
@NonNull
@@ -366,4 +369,13 @@
public String deepToString(@NonNull String indent) {
return indent + toString();
}
+
+ /**
+ * Enable or disable the shader
+ *
+ * @param shaderValid if true shader can be used
+ */
+ public void enable(boolean shaderValid) {
+ mShaderValid = shaderValid;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
index 6c9105d..e9aae1e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
@@ -64,6 +64,7 @@
@Override
public void apply(@NonNull RemoteContext context) {
context.setTheme(mTheme);
+ markDirty();
}
@NonNull
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
index dcf1d25..e05bdf2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
@@ -106,6 +106,7 @@
for (Operation op : mList) {
if (op instanceof TextData) {
op.apply(context);
+ context.incrementOpCount();
}
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
index e95dfda..8a77dc3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
@@ -66,6 +66,34 @@
protected float mZIndex = 0f;
+ private boolean mNeedsBoundsAnimation = false;
+
+ /**
+ * Mark the component as needing a bounds animation pass
+ */
+ public void markNeedsBoundsAnimation() {
+ mNeedsBoundsAnimation = true;
+ if (mParent != null && !mParent.mNeedsBoundsAnimation) {
+ mParent.markNeedsBoundsAnimation();
+ }
+ }
+
+ /**
+ * Clear the bounds animation pass flag
+ */
+ public void clearNeedsBoundsAnimation() {
+ mNeedsBoundsAnimation = false;
+ }
+
+ /**
+ * True if needs a bounds animation
+ *
+ * @return true if needs a bounds animation pass
+ */
+ public boolean needsBoundsAnimation() {
+ return mNeedsBoundsAnimation;
+ }
+
public float getZIndex() {
return mZIndex;
}
@@ -382,12 +410,40 @@
} else {
mVisibility = m.getVisibility();
}
- setWidth(m.getW());
- setHeight(m.getH());
- setLayoutPosition(m.getX(), m.getY());
+ if (mAnimateMeasure == null) {
+ setWidth(m.getW());
+ setHeight(m.getH());
+ setLayoutPosition(m.getX(), m.getY());
+ updateComponentValues(context);
+ clearNeedsBoundsAnimation();
+ } else {
+ mAnimateMeasure.apply(context);
+ updateComponentValues(context);
+ markNeedsBoundsAnimation();
+ }
mFirstLayout = false;
}
+ /**
+ * Animate the bounds of the component as needed
+ * @param context
+ */
+ public void animatingBounds(@NonNull RemoteContext context) {
+ if (mAnimateMeasure != null) {
+ mAnimateMeasure.apply(context);
+ updateComponentValues(context);
+ markNeedsBoundsAnimation();
+ } else {
+ clearNeedsBoundsAnimation();
+ }
+ for (Operation op : mList) {
+ if (op instanceof Measurable) {
+ Measurable m = (Measurable) op;
+ m.animatingBounds(context);
+ }
+ }
+ }
+
@NonNull public float[] locationInWindow = new float[2];
public boolean contains(float x, float y) {
@@ -698,9 +754,6 @@
}
public void paintingComponent(@NonNull PaintContext context) {
- if (!mComponentValues.isEmpty()) {
- updateComponentValues(context.getContext());
- }
if (mPreTranslate != null) {
mPreTranslate.paint(context);
}
@@ -718,8 +771,10 @@
}
if (op instanceof PaintOperation) {
((PaintOperation) op).paint(context);
+ context.getContext().incrementOpCount();
} else {
op.apply(context.getContext());
+ context.getContext().incrementOpCount();
}
}
context.restore();
@@ -728,7 +783,7 @@
public boolean applyAnimationAsNeeded(@NonNull PaintContext context) {
if (context.isAnimationEnabled() && mAnimateMeasure != null) {
- mAnimateMeasure.apply(context);
+ mAnimateMeasure.paint(context);
context.needsRepaint();
return true;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
index e25392c..9103885 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
@@ -286,7 +286,9 @@
@Override
public void paintingComponent(@NonNull PaintContext context) {
Component prev = context.getContext().mLastComponent;
- context.getContext().mLastComponent = this;
+ RemoteContext remoteContext = context.getContext();
+
+ remoteContext.mLastComponent = this;
context.save();
context.translate(mX, mY);
if (context.isVisualDebug()) {
@@ -329,6 +331,7 @@
child.updateVariables(context.getContext());
child.markNotDirty();
}
+ remoteContext.incrementOpCount();
child.paint(context);
}
} else {
@@ -337,6 +340,7 @@
child.updateVariables(context.getContext());
child.markNotDirty();
}
+ remoteContext.incrementOpCount();
child.paint(context);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java
index 505656e..9fc5da8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java
@@ -57,6 +57,7 @@
for (Operation op : mList) {
if (op instanceof TextData) {
op.apply(context);
+ context.incrementOpCount();
}
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
index 1b85681..ab1e0ac 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
@@ -107,9 +107,11 @@
@Override
public void paint(@NonNull PaintContext context) {
+ RemoteContext remoteContext = context.getContext();
if (mIndexVariableId == 0) {
for (float i = mFromOut; i < mUntilOut; i += mStepOut) {
for (Operation op : mList) {
+ remoteContext.incrementOpCount();
op.apply(context.getContext());
}
}
@@ -120,6 +122,7 @@
if (op instanceof VariableSupport && op.isDirty()) {
((VariableSupport) op).updateVariables(context.getContext());
}
+ remoteContext.incrementOpCount();
op.apply(context.getContext());
}
}
@@ -172,4 +175,16 @@
.field(DocumentedOperation.FLOAT, "step", "value step")
.field(DocumentedOperation.FLOAT, "until", "stops less than or equal");
}
+
+ /**
+ * Calculate and estimate of the number of iterations
+ *
+ * @return number of loops or 10 if based on variables
+ */
+ public int estimateIterations() {
+ if (!(Float.isNaN(mUntil) || Float.isNaN(mFrom) || Float.isNaN(mStep))) {
+ return (int) (0.5f + (mUntil - mFrom) / mStep);
+ }
+ return 10; // this is a generic estmate if the values are variables;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
index 11c0f3f..11fa7ee 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
@@ -137,8 +137,8 @@
return;
}
context.mLastComponent = this;
- mWidth = context.mWidth;
- mHeight = context.mHeight;
+ setWidth(context.mWidth);
+ setHeight(context.mHeight);
// TODO: reuse MeasurePass
MeasurePass measurePass = new MeasurePass();
@@ -155,7 +155,9 @@
@Override
public void paint(@NonNull PaintContext context) {
mNeedsRepaint = false;
- context.getContext().mLastComponent = this;
+ RemoteContext remoteContext = context.getContext();
+ remoteContext.mLastComponent = this;
+
context.save();
if (mParent == null) { // root layout
@@ -165,6 +167,7 @@
for (Operation op : mList) {
if (op instanceof PaintOperation) {
((PaintOperation) op).paint(context);
+ remoteContext.incrementOpCount();
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
index 2af3c73..1de956b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
@@ -19,6 +19,7 @@
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
import com.android.internal.widget.remotecompose.core.operations.layout.DecoratorComponent;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
@@ -103,13 +104,18 @@
@NonNull public PaintBundle paint = new PaintBundle();
- public void apply(@NonNull PaintContext context) {
- update(context.getContext().currentTime);
-
+ /**
+ * Apply the layout portion of the animation if any
+ *
+ * @param context
+ */
+ public void apply(@NonNull RemoteContext context) {
+ update(context.currentTime);
mComponent.setX(getX());
mComponent.setY(getY());
mComponent.setWidth(getWidth());
mComponent.setHeight(getHeight());
+ mComponent.updateVariables(context);
float w = mComponent.getWidth();
float h = mComponent.getHeight();
@@ -120,10 +126,17 @@
h -= pop.getTop() + pop.getBottom();
}
if (op instanceof DecoratorComponent) {
- ((DecoratorComponent) op).layout(context.getContext(), mComponent, w, h);
+ ((DecoratorComponent) op).layout(context, mComponent, w, h);
}
}
+ }
+ /**
+ * Paint the transition animation for the component owned
+ *
+ * @param context
+ */
+ public void paint(@NonNull PaintContext context) {
if (mOriginal.getVisibility() != mTarget.getVisibility()) {
if (mTarget.getVisibility() == Component.Visibility.GONE) {
switch (mExitAnimation) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java
index fbf2784..a999874 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java
@@ -44,4 +44,11 @@
* @return true if need to remeasured, false otherwise
*/
boolean needsMeasure();
+
+ /**
+ * Animate bounds of the component
+ *
+ * @param context
+ */
+ void animatingBounds(RemoteContext context);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
index d2ba13f..a1609ac 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
@@ -49,6 +49,7 @@
super.apply(context);
for (ModifierOperation op : mList) {
op.apply(context);
+ context.incrementOpCount();
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
index 276a506..6eb83f1 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
@@ -110,6 +110,7 @@
provideHapticFeedback(type);
}
});
+ mInner.checkShaders(mShaderControl);
}
/**
@@ -249,8 +250,23 @@
mInner.clearLocalString("SYSTEM:" + name);
}
+ /**
+ * This is the number of ops used to calculate the last frame.
+ *
+ * @return number of ops
+ */
+ public int getOpsPerFrame() {
+ return mInner.getDocument().mDocument.getOpsPerFrame();
+ }
+
/** Id action callback interface */
public interface IdActionCallbacks {
+ /**
+ * Callback for on action
+ *
+ * @param id the id of the action
+ * @param metadata the metadata of the action
+ */
void onAction(int id, String metadata);
}
@@ -684,4 +700,19 @@
public float getEvalTime() {
return mInner.getEvalTime();
}
+
+ private CoreDocument.ShaderControl mShaderControl =
+ (shader) -> {
+ return false;
+ };
+
+ /**
+ * Sets the controller for shaders. Note set before loading the document. The default is to not
+ * accept shaders.
+ *
+ * @param ctl the controller
+ */
+ public void setShaderControl(CoreDocument.ShaderControl ctl) {
+ mShaderControl = ctl;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
index daa44c8..0712ea4 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
@@ -505,6 +505,9 @@
return;
}
ShaderData data = getShaderData(shaderId);
+ if (data == null) {
+ return;
+ }
RuntimeShader shader = new RuntimeShader(getText(data.getShaderTextId()));
String[] names = data.getUniformFloatNames();
for (int i = 0; i < names.length; i++) {
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
index 5ba6283..c7b1166 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
@@ -205,6 +205,12 @@
return count;
}
+ /**
+ * set a float externally
+ *
+ * @param id
+ * @param value
+ */
public void setExternalFloat(int id, float value) {
mARContext.loadFloat(id, value);
}
@@ -221,6 +227,15 @@
return mDocument.getDocument().hasTouchListener();
}
+ /**
+ * Check shaders and disable them
+ *
+ * @param shaderControl the callback to validate the shader
+ */
+ public void checkShaders(CoreDocument.ShaderControl shaderControl) {
+ mDocument.getDocument().checkShaders(mARContext, shaderControl);
+ }
+
public interface ClickCallbacks {
void click(int id, String metadata);
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index e22d958..8e3303a 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -92,6 +92,7 @@
"android_view_VelocityTracker.cpp",
"android_view_VerifiedKeyEvent.cpp",
"android_view_VerifiedMotionEvent.cpp",
+ "com_android_internal_util_ArrayUtils.cpp",
"com_android_internal_util_VirtualRefBasePtr.cpp",
"core_jni_helpers.cpp",
":deviceproductinfoconstants_aidl",
@@ -220,6 +221,7 @@
"android_hardware_camera2_utils_SurfaceUtils.cpp",
"android_hardware_display_DisplayManagerGlobal.cpp",
"android_hardware_display_DisplayViewport.cpp",
+ "android_hardware_display_DisplayTopology.cpp",
"android_hardware_HardwareBuffer.cpp",
"android_hardware_OverlayProperties.cpp",
"android_hardware_SensorManager.cpp",
@@ -279,6 +281,7 @@
"libasync_safe",
"libbinderthreadstateutils",
"libdmabufinfo",
+ "libgenfslabelsversion.ffi",
"libgui_window_info_static",
"libkernelconfigs",
"libnativehelper_lazy",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index ac187b0..78d69f0 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -220,6 +220,7 @@
extern int register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
extern int register_com_android_internal_security_VerityUtils(JNIEnv* env);
+extern int register_com_android_internal_util_ArrayUtils(JNIEnv* env);
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
extern int register_android_window_WindowInfosListener(JNIEnv* env);
extern int register_android_window_ScreenCapture(JNIEnv* env);
@@ -1621,6 +1622,7 @@
REG_JNI(register_com_android_internal_os_ZygoteCommandBuffer),
REG_JNI(register_com_android_internal_os_ZygoteInit),
REG_JNI(register_com_android_internal_security_VerityUtils),
+ REG_JNI(register_com_android_internal_util_ArrayUtils),
REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
REG_JNI(register_android_hardware_Camera),
REG_JNI(register_android_hardware_camera2_CameraMetadata),
diff --git a/core/jni/android_hardware_display_DisplayTopology.cpp b/core/jni/android_hardware_display_DisplayTopology.cpp
new file mode 100644
index 0000000..d9e802d
--- /dev/null
+++ b/core/jni/android_hardware_display_DisplayTopology.cpp
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "DisplayTopology-JNI"
+
+#include <android_hardware_display_DisplayTopology.h>
+#include <nativehelper/ScopedLocalRef.h>
+#include <utils/Errors.h>
+
+#include "jni_wrappers.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static struct {
+ jclass clazz;
+ jfieldID primaryDisplayId;
+ jfieldID displayNodes;
+} gDisplayTopologyGraphClassInfo;
+
+static struct {
+ jclass clazz;
+ jfieldID displayId;
+ jfieldID adjacentDisplays;
+} gDisplayTopologyGraphNodeClassInfo;
+
+static struct {
+ jclass clazz;
+ jfieldID displayId;
+ jfieldID position;
+ jfieldID offsetPx;
+} gDisplayTopologyGraphAdjacentDisplayClassInfo;
+
+// ----------------------------------------------------------------------------
+
+status_t android_hardware_display_DisplayTopologyAdjacentDisplay_toNative(
+ JNIEnv* env, jobject adjacentDisplayObj, DisplayTopologyAdjacentDisplay* adjacentDisplay) {
+ adjacentDisplay->displayId = ui::LogicalDisplayId{
+ env->GetIntField(adjacentDisplayObj,
+ gDisplayTopologyGraphAdjacentDisplayClassInfo.displayId)};
+ adjacentDisplay->position = static_cast<DisplayTopologyPosition>(
+ env->GetIntField(adjacentDisplayObj,
+ gDisplayTopologyGraphAdjacentDisplayClassInfo.position));
+ adjacentDisplay->offsetPx =
+ env->GetFloatField(adjacentDisplayObj,
+ gDisplayTopologyGraphAdjacentDisplayClassInfo.offsetPx);
+ return OK;
+}
+
+status_t android_hardware_display_DisplayTopologyGraphNode_toNative(
+ JNIEnv* env, jobject nodeObj,
+ std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>&
+ graph) {
+ ui::LogicalDisplayId displayId = ui::LogicalDisplayId{
+ env->GetIntField(nodeObj, gDisplayTopologyGraphNodeClassInfo.displayId)};
+
+ jobjectArray adjacentDisplaysArray = static_cast<jobjectArray>(
+ env->GetObjectField(nodeObj, gDisplayTopologyGraphNodeClassInfo.adjacentDisplays));
+
+ if (adjacentDisplaysArray) {
+ jsize length = env->GetArrayLength(adjacentDisplaysArray);
+ for (jsize i = 0; i < length; i++) {
+ ScopedLocalRef<jobject>
+ adjacentDisplayObj(env, env->GetObjectArrayElement(adjacentDisplaysArray, i));
+ if (NULL != adjacentDisplayObj.get()) {
+ break; // found null element indicating end of used portion of the array
+ }
+
+ DisplayTopologyAdjacentDisplay adjacentDisplay;
+ android_hardware_display_DisplayTopologyAdjacentDisplay_toNative(env,
+ adjacentDisplayObj
+ .get(),
+ &adjacentDisplay);
+ graph[displayId].push_back(adjacentDisplay);
+ }
+ }
+ return OK;
+}
+
+DisplayTopologyGraph android_hardware_display_DisplayTopologyGraph_toNative(JNIEnv* env,
+ jobject topologyObj) {
+ DisplayTopologyGraph topology;
+ topology.primaryDisplayId = ui::LogicalDisplayId{
+ env->GetIntField(topologyObj, gDisplayTopologyGraphClassInfo.primaryDisplayId)};
+
+ jobjectArray nodesArray = static_cast<jobjectArray>(
+ env->GetObjectField(topologyObj, gDisplayTopologyGraphClassInfo.displayNodes));
+
+ if (nodesArray) {
+ jsize length = env->GetArrayLength(nodesArray);
+ for (jsize i = 0; i < length; i++) {
+ ScopedLocalRef<jobject> nodeObj(env, env->GetObjectArrayElement(nodesArray, i));
+ if (NULL != nodeObj.get()) {
+ break; // found null element indicating end of used portion of the array
+ }
+
+ android_hardware_display_DisplayTopologyGraphNode_toNative(env, nodeObj.get(),
+ topology.graph);
+ }
+ }
+ return topology;
+}
+
+// ----------------------------------------------------------------------------
+
+int register_android_hardware_display_DisplayTopology(JNIEnv* env) {
+ jclass graphClazz = FindClassOrDie(env, "android/hardware/display/DisplayTopologyGraph");
+ gDisplayTopologyGraphClassInfo.clazz = MakeGlobalRefOrDie(env, graphClazz);
+
+ gDisplayTopologyGraphClassInfo.primaryDisplayId =
+ GetFieldIDOrDie(env, gDisplayTopologyGraphClassInfo.clazz, "primaryDisplayId", "I");
+ gDisplayTopologyGraphClassInfo.displayNodes =
+ GetFieldIDOrDie(env, gDisplayTopologyGraphClassInfo.clazz, "displayNodes",
+ "[Landroid/hardware/display/DisplayTopologyGraph$DisplayNode;");
+
+ jclass displayNodeClazz =
+ FindClassOrDie(env, "android/hardware/display/DisplayTopologyGraph$DisplayNode");
+ gDisplayTopologyGraphNodeClassInfo.clazz = MakeGlobalRefOrDie(env, displayNodeClazz);
+ gDisplayTopologyGraphNodeClassInfo.displayId =
+ GetFieldIDOrDie(env, gDisplayTopologyGraphNodeClassInfo.clazz, "displayId", "I");
+ gDisplayTopologyGraphNodeClassInfo.adjacentDisplays =
+ GetFieldIDOrDie(env, gDisplayTopologyGraphNodeClassInfo.clazz, "adjacentDisplays",
+ "[Landroid/hardware/display/DisplayTopologyGraph$AdjacentDisplay;");
+
+ jclass adjacentDisplayClazz =
+ FindClassOrDie(env, "android/hardware/display/DisplayTopologyGraph$AdjacentDisplay");
+ gDisplayTopologyGraphAdjacentDisplayClassInfo.clazz =
+ MakeGlobalRefOrDie(env, adjacentDisplayClazz);
+ gDisplayTopologyGraphAdjacentDisplayClassInfo.displayId =
+ GetFieldIDOrDie(env, gDisplayTopologyGraphAdjacentDisplayClassInfo.clazz, "displayId",
+ "I");
+ gDisplayTopologyGraphAdjacentDisplayClassInfo.position =
+ GetFieldIDOrDie(env, gDisplayTopologyGraphAdjacentDisplayClassInfo.clazz, "position",
+ "I");
+ gDisplayTopologyGraphAdjacentDisplayClassInfo.offsetPx =
+ GetFieldIDOrDie(env, gDisplayTopologyGraphAdjacentDisplayClassInfo.clazz, "offsetPx",
+ "F");
+ return 0;
+}
+
+} // namespace android
diff --git a/core/jni/android_hardware_display_DisplayTopology.h b/core/jni/android_hardware_display_DisplayTopology.h
new file mode 100644
index 0000000..390191f
--- /dev/null
+++ b/core/jni/android_hardware_display_DisplayTopology.h
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <input/DisplayTopologyGraph.h>
+
+#include "jni.h"
+
+namespace android {
+
+/**
+ * Copies the contents of a DVM DisplayTopology object to a new native DisplayTopology instance.
+ * Returns DisplayTopology.
+ */
+extern DisplayTopologyGraph android_hardware_display_DisplayTopologyGraph_toNative(
+ JNIEnv* env, jobject eventObj);
+
+} // namespace android
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index 704aef3..4ba1ae9 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -51,6 +51,18 @@
#define ENCODING_DTS_UHD_P2 30
#define ENCODING_DSD 31
#define ENCODING_AC4_L4 32
+#define ENCODING_IAMF_SIMPLE_PROFILE_OPUS 33
+#define ENCODING_IAMF_SIMPLE_PROFILE_AAC 34
+#define ENCODING_IAMF_SIMPLE_PROFILE_FLAC 35
+#define ENCODING_IAMF_SIMPLE_PROFILE_PCM 36
+#define ENCODING_IAMF_BASE_PROFILE_OPUS 37
+#define ENCODING_IAMF_BASE_PROFILE_AAC 38
+#define ENCODING_IAMF_BASE_PROFILE_FLAC 39
+#define ENCODING_IAMF_BASE_PROFILE_PCM 40
+#define ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS 41
+#define ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC 42
+#define ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC 43
+#define ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM 44
#define ENCODING_INVALID 0
#define ENCODING_DEFAULT 1
@@ -128,6 +140,30 @@
return AUDIO_FORMAT_DTS_UHD_P2;
case ENCODING_DSD:
return AUDIO_FORMAT_DSD;
+ case ENCODING_IAMF_SIMPLE_PROFILE_OPUS:
+ return AUDIO_FORMAT_IAMF_SIMPLE_OPUS;
+ case ENCODING_IAMF_SIMPLE_PROFILE_AAC:
+ return AUDIO_FORMAT_IAMF_SIMPLE_AAC;
+ case ENCODING_IAMF_SIMPLE_PROFILE_FLAC:
+ return AUDIO_FORMAT_IAMF_SIMPLE_FLAC;
+ case ENCODING_IAMF_SIMPLE_PROFILE_PCM:
+ return AUDIO_FORMAT_IAMF_SIMPLE_PCM;
+ case ENCODING_IAMF_BASE_PROFILE_OPUS:
+ return AUDIO_FORMAT_IAMF_BASE_OPUS;
+ case ENCODING_IAMF_BASE_PROFILE_AAC:
+ return AUDIO_FORMAT_IAMF_BASE_AAC;
+ case ENCODING_IAMF_BASE_PROFILE_FLAC:
+ return AUDIO_FORMAT_IAMF_BASE_FLAC;
+ case ENCODING_IAMF_BASE_PROFILE_PCM:
+ return AUDIO_FORMAT_IAMF_BASE_PCM;
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS:
+ return AUDIO_FORMAT_IAMF_BASE_ENHANCED_OPUS;
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC:
+ return AUDIO_FORMAT_IAMF_BASE_ENHANCED_AAC;
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC:
+ return AUDIO_FORMAT_IAMF_BASE_ENHANCED_FLAC;
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM:
+ return AUDIO_FORMAT_IAMF_BASE_ENHANCED_PCM;
default:
return AUDIO_FORMAT_INVALID;
}
@@ -211,6 +247,30 @@
return ENCODING_DEFAULT;
case AUDIO_FORMAT_DSD:
return ENCODING_DSD;
+ case AUDIO_FORMAT_IAMF_SIMPLE_OPUS:
+ return ENCODING_IAMF_SIMPLE_PROFILE_OPUS;
+ case AUDIO_FORMAT_IAMF_SIMPLE_AAC:
+ return ENCODING_IAMF_SIMPLE_PROFILE_AAC;
+ case AUDIO_FORMAT_IAMF_SIMPLE_FLAC:
+ return ENCODING_IAMF_SIMPLE_PROFILE_FLAC;
+ case AUDIO_FORMAT_IAMF_SIMPLE_PCM:
+ return ENCODING_IAMF_SIMPLE_PROFILE_PCM;
+ case AUDIO_FORMAT_IAMF_BASE_OPUS:
+ return ENCODING_IAMF_BASE_PROFILE_OPUS;
+ case AUDIO_FORMAT_IAMF_BASE_AAC:
+ return ENCODING_IAMF_BASE_PROFILE_AAC;
+ case AUDIO_FORMAT_IAMF_BASE_FLAC:
+ return ENCODING_IAMF_BASE_PROFILE_FLAC;
+ case AUDIO_FORMAT_IAMF_BASE_PCM:
+ return ENCODING_IAMF_BASE_PROFILE_PCM;
+ case AUDIO_FORMAT_IAMF_BASE_ENHANCED_OPUS:
+ return ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS;
+ case AUDIO_FORMAT_IAMF_BASE_ENHANCED_AAC:
+ return ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC;
+ case AUDIO_FORMAT_IAMF_BASE_ENHANCED_FLAC:
+ return ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC;
+ case AUDIO_FORMAT_IAMF_BASE_ENHANCED_PCM:
+ return ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM;
default:
return ENCODING_INVALID;
}
diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp
index 7a4670f4..cd39e6f 100644
--- a/core/jni/android_os_SELinux.cpp
+++ b/core/jni/android_os_SELinux.cpp
@@ -18,38 +18,43 @@
#include <errno.h>
#include <fcntl.h>
-
-#include <utils/Log.h>
-
+#include <genfslabelsversion.h>
#include <nativehelper/JNIPlatformHelp.h>
-#include "jni.h"
-#include "core_jni_helpers.h"
-#include "selinux/selinux.h"
-#include "selinux/android.h"
-#include <memory>
-#include <atomic>
#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedUtfChars.h>
+#include <utils/Log.h>
+
+#include <atomic>
+#include <memory>
+
+#include "core_jni_helpers.h"
+#include "jni.h"
+#include "selinux/android.h"
+#include "selinux/selinux.h"
namespace android {
namespace {
-std::atomic<selabel_handle*> sehandle{nullptr};
+std::atomic<selabel_handle *> file_sehandle{nullptr};
-selabel_handle* GetSELabelHandle() {
- selabel_handle* h = sehandle.load();
+selabel_handle *GetSELabelHandle_impl(selabel_handle *(*handle_func)(),
+ std::atomic<selabel_handle *> *handle_cache) {
+ selabel_handle *h = handle_cache->load();
if (h != nullptr) {
return h;
}
- h = selinux_android_file_context_handle();
+ h = handle_func();
selabel_handle* expected = nullptr;
- if (!sehandle.compare_exchange_strong(expected, h)) {
+ if (!handle_cache->compare_exchange_strong(expected, h)) {
selabel_close(h);
- return sehandle.load();
+ return handle_cache->load();
}
return h;
}
+selabel_handle *GetSELabelFileBackendHandle() {
+ return GetSELabelHandle_impl(selinux_android_file_context_handle, &file_sehandle);
+}
}
struct SecurityContext_Delete {
@@ -105,7 +110,7 @@
return NULL;
}
- auto* selabel_handle = GetSELabelHandle();
+ auto *selabel_handle = GetSELabelFileBackendHandle();
if (selabel_handle == NULL) {
ALOGE("fileSelabelLookup => Failed to get SEHandle");
return NULL;
@@ -404,8 +409,19 @@
}
/*
+ * Function: getGenfsLabelsVersion
+ * Purpose: get which genfs labels version /vendor uses
+ * Returns: int: genfs labels version of /vendor
+ * Exceptions: none
+ */
+static jint getGenfsLabelsVersion(JNIEnv *, jclass) {
+ return get_genfs_labels_version();
+}
+
+/*
* JNI registration.
*/
+// clang-format off
static const JNINativeMethod method_table[] = {
/* name, signature, funcPtr */
{ "checkSELinuxAccess" , "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z" , (void*)checkSELinuxAccess },
@@ -420,7 +436,9 @@
{ "setFileContext" , "(Ljava/lang/String;Ljava/lang/String;)Z" , (void*)setFileCon },
{ "setFSCreateContext" , "(Ljava/lang/String;)Z" , (void*)setFSCreateCon },
{ "fileSelabelLookup" , "(Ljava/lang/String;)Ljava/lang/String;" , (void*)fileSelabelLookup},
+ { "getGenfsLabelsVersion" , "()I" , (void *)getGenfsLabelsVersion},
};
+// clang-format on
static int log_callback(int type, const char *fmt, ...) {
va_list ap;
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index b2eeff3..f40cfd9f 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -532,7 +532,12 @@
static const size_t kPageSize = getpagesize();
// App compat is only applicable on 16kb-page-size devices.
- return kPageSize == 0x4000;
+ if (kPageSize != 0x4000) {
+ return false;
+ }
+
+ // Explicit disabled status for app compat
+ return !android::base::GetBoolProperty("pm.16kb.app_compat.disabled", false);
}
static jint
diff --git a/core/jni/com_android_internal_util_ArrayUtils.cpp b/core/jni/com_android_internal_util_ArrayUtils.cpp
new file mode 100644
index 0000000..c706258
--- /dev/null
+++ b/core/jni/com_android_internal_util_ArrayUtils.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ArrayUtils"
+
+#include <android-base/logging.h>
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <string.h>
+#include <unistd.h>
+#include <utils/Log.h>
+
+namespace android {
+
+static size_t GetCacheLineSize() {
+ long size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
+ if (size <= 0) {
+ ALOGE("Unable to determine L1 data cache line size. Assuming 32 bytes");
+ return 32;
+ }
+ // The cache line size should always be a power of 2.
+ CHECK((size & (size - 1)) == 0);
+
+ return size;
+}
+
+static void CleanCacheLineContainingAddress(const uint8_t* p) {
+#if defined(__aarch64__)
+ // 'dc cvac' stands for "Data Cache line Clean by Virtual Address to point-of-Coherency".
+ // It writes the cache line back to the "point-of-coherency", i.e. main memory.
+ asm volatile("dc cvac, %0" ::"r"(p));
+#elif defined(__i386__) || defined(__x86_64__)
+ asm volatile("clflush (%0)" ::"r"(p));
+#elif defined(__riscv)
+ // This should eventually work, but it is not ready to be enabled yet:
+ // 1.) The Android emulator needs to add support for zicbom.
+ // 2.) Kernel needs to enable zicbom in usermode.
+ // 3.) Android clang needs to add zicbom to the target.
+ // asm volatile("cbo.clean (%0)" ::"r"(p));
+#elif defined(__arm__)
+ // arm32 has a cacheflush() syscall, but it is undocumented and only flushes the icache.
+ // It is not the same as cacheflush(2) as documented in the Linux man-pages project.
+#else
+#error "Unknown architecture"
+#endif
+}
+
+static void CleanDataCache(const uint8_t* p, size_t buffer_size, size_t cache_line_size) {
+ // Clean the first line that overlaps the buffer.
+ CleanCacheLineContainingAddress(p);
+ // Clean any additional lines that overlap the buffer. Use cache-line-aligned addresses to
+ // ensure that (a) the last cache line gets flushed, and (b) no cache line is flushed twice.
+ for (size_t i = cache_line_size - ((uintptr_t)p & (cache_line_size - 1)); i < buffer_size;
+ i += cache_line_size) {
+ CleanCacheLineContainingAddress(p + i);
+ }
+}
+
+static void ZeroizePrimitiveArray(JNIEnv* env, jclass clazz, jarray array, size_t component_len) {
+ static const size_t cache_line_size = GetCacheLineSize();
+
+ if (array == nullptr) {
+ return;
+ }
+
+ size_t buffer_size = env->GetArrayLength(array) * component_len;
+ if (buffer_size == 0) {
+ return;
+ }
+
+ // ART guarantees that GetPrimitiveArrayCritical never copies.
+ jboolean isCopy;
+ void* elems = env->GetPrimitiveArrayCritical(array, &isCopy);
+ CHECK(!isCopy);
+
+#ifdef __BIONIC__
+ memset_explicit(elems, 0, buffer_size);
+#else
+ memset(elems, 0, buffer_size);
+#endif
+ // Clean the data cache so that the data gets zeroized in main memory right away. Without this,
+ // it might not be written to main memory until the cache line happens to be evicted.
+ CleanDataCache(static_cast<const uint8_t*>(elems), buffer_size, cache_line_size);
+
+ env->ReleasePrimitiveArrayCritical(array, elems, /* mode= */ 0);
+}
+
+static void ZeroizeByteArray(JNIEnv* env, jclass clazz, jbyteArray array) {
+ ZeroizePrimitiveArray(env, clazz, array, sizeof(jbyte));
+}
+
+static void ZeroizeCharArray(JNIEnv* env, jclass clazz, jcharArray array) {
+ ZeroizePrimitiveArray(env, clazz, array, sizeof(jchar));
+}
+
+static const JNINativeMethod sMethods[] = {
+ {"zeroize", "([B)V", (void*)ZeroizeByteArray},
+ {"zeroize", "([C)V", (void*)ZeroizeCharArray},
+};
+
+int register_com_android_internal_util_ArrayUtils(JNIEnv* env) {
+ return jniRegisterNativeMethods(env, "com/android/internal/util/ArrayUtils", sMethods,
+ NELEM(sMethods));
+}
+
+} // namespace android
diff --git a/core/res/Android.bp b/core/res/Android.bp
index 903d08b..be4fb8b 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -179,6 +179,7 @@
"art-aconfig-flags",
"ranging_aconfig_flags",
"aconfig_settingslib_flags",
+ "telephony_flags",
],
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a86d551..6b8056c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4149,6 +4149,14 @@
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION"
android:protectionLevel="internal|role" />
+ <!-- Allows an application to manage policy related to AppFunctions.
+ <p>Protection level: internal|role
+ @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_FUNCTIONS"
+ android:featureFlag="android.app.appfunctions.flags.enable_app_function_manager"
+ android:protectionLevel="internal|role" />
+
<!-- Allows an application to set policy related to subscriptions downloaded by an admin.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
@@ -6845,6 +6853,13 @@
<permission android:name="android.permission.BATTERY_STATS"
android:protectionLevel="signature|privileged|development" />
+ <!-- @SystemApi @hide Allows an application to collect high-precision PowerMonitor readings
+ <p>Protection level: signature|privileged|development
+ @FlaggedApi(android.permission.flags.Flags.FLAG_FINE_POWER_MONITOR_PERMISSION) -->
+ <permission android:name="android.permission.ACCESS_FINE_POWER_MONITORS"
+ android:protectionLevel="signature|privileged|development"
+ android:featureFlag="android.permission.flags.fine_power_monitor_permission" />
+
<!--Allows an application to manage statscompanion.
<p>Not for use by third-party applications.
@hide -->
@@ -7034,6 +7049,13 @@
<permission android:name="android.permission.MANAGE_SUBSCRIPTION_PLANS"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi @hide Allows for reading subscription plan fields for status and end date.
+ @FlaggedApi(com.android.internal.telephony.flags.Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ -->
+ <permission android:name="android.permission.READ_SUBSCRIPTION_PLANS"
+ android:protectionLevel="signature|privileged"
+ android:featureFlag="com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date" />
+
<!-- C2DM permission.
@hide Used internally.
-->
@@ -8777,6 +8799,17 @@
android:protectionLevel="signature|privileged|vendorPrivileged"
android:featureFlag="android.media.tv.flags.kids_mode_tvdb_sharing"/>
+ <!-- @SystemApi
+ @FlaggedApi("android.permission.flags.text_classifier_choice_api_enabled")
+ This permission is required to access the specific text classifier you need from the
+ TextClassificationManager.
+ <p>Protection level: signature|role
+ @hide
+ -->
+ <permission android:name="android.permission.ACCESS_TEXT_CLASSIFIER_BY_TYPE"
+ android:protectionLevel="signature|role"
+ android:featureFlag="android.permission.flags.text_classifier_choice_api_enabled"/>
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/res/layout/notification_2025_template_collapsed_base.xml b/core/res/res/layout/notification_2025_template_collapsed_base.xml
index 09c02c9..76c810b 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_base.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml
@@ -28,8 +28,8 @@
android:id="@+id/left_icon"
android:layout_width="@dimen/notification_2025_left_icon_size"
android:layout_height="@dimen/notification_2025_left_icon_size"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="@dimen/notification_left_icon_start"
+ android:layout_alignParentStart="true"
+ android:layout_margin="@dimen/notification_2025_margin"
android:background="@drawable/notification_large_icon_outline"
android:clipToOutline="true"
android:importantForAccessibility="no"
@@ -41,8 +41,8 @@
android:id="@+id/icon"
android:layout_width="@dimen/notification_2025_icon_circle_size"
android:layout_height="@dimen/notification_2025_icon_circle_size"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:layout_alignParentStart="true"
+ android:layout_margin="@dimen/notification_2025_margin"
android:background="@drawable/notification_icon_circle"
android:padding="@dimen/notification_2025_icon_circle_padding"
android:maxDrawableWidth="@dimen/notification_2025_icon_circle_size"
diff --git a/core/res/res/layout/notification_2025_template_collapsed_media.xml b/core/res/res/layout/notification_2025_template_collapsed_media.xml
index f539105..2e0a7af 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_media.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_media.xml
@@ -32,8 +32,8 @@
android:id="@+id/left_icon"
android:layout_width="@dimen/notification_2025_left_icon_size"
android:layout_height="@dimen/notification_2025_left_icon_size"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="@dimen/notification_left_icon_start"
+ android:layout_alignParentStart="true"
+ android:layout_margin="@dimen/notification_2025_margin"
android:background="@drawable/notification_large_icon_outline"
android:clipToOutline="true"
android:importantForAccessibility="no"
@@ -45,8 +45,8 @@
android:id="@+id/icon"
android:layout_width="@dimen/notification_2025_icon_circle_size"
android:layout_height="@dimen/notification_2025_icon_circle_size"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:layout_alignParentStart="true"
+ android:layout_margin="@dimen/notification_2025_margin"
android:background="@drawable/notification_icon_circle"
android:padding="@dimen/notification_2025_icon_circle_padding"
/>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
index ddf3ebc..f644ade 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
@@ -46,8 +46,8 @@
android:id="@+id/left_icon"
android:layout_width="@dimen/notification_2025_left_icon_size"
android:layout_height="@dimen/notification_2025_left_icon_size"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="@dimen/notification_left_icon_start"
+ android:layout_alignParentStart="true"
+ android:layout_margin="@dimen/notification_2025_margin"
android:background="@drawable/notification_large_icon_outline"
android:clipToOutline="true"
android:importantForAccessibility="no"
@@ -59,8 +59,8 @@
android:id="@+id/icon"
android:layout_width="@dimen/notification_2025_icon_circle_size"
android:layout_height="@dimen/notification_2025_icon_circle_size"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:layout_alignParentStart="true"
+ android:layout_margin="@dimen/notification_2025_margin"
android:background="@drawable/notification_icon_circle"
android:padding="@dimen/notification_2025_icon_circle_padding"
/>
diff --git a/core/res/res/layout/notification_2025_template_header.xml b/core/res/res/layout/notification_2025_template_header.xml
index b7fe454..63872af 100644
--- a/core/res/res/layout/notification_2025_template_header.xml
+++ b/core/res/res/layout/notification_2025_template_header.xml
@@ -33,8 +33,7 @@
android:layout_width="@dimen/notification_2025_left_icon_size"
android:layout_height="@dimen/notification_2025_left_icon_size"
android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- android:layout_marginStart="@dimen/notification_left_icon_start"
+ android:layout_margin="@dimen/notification_2025_margin"
android:background="@drawable/notification_large_icon_outline"
android:clipToOutline="true"
android:importantForAccessibility="no"
@@ -47,8 +46,7 @@
android:layout_width="@dimen/notification_2025_icon_circle_size"
android:layout_height="@dimen/notification_2025_icon_circle_size"
android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:layout_margin="@dimen/notification_2025_margin"
android:background="@drawable/notification_icon_circle"
android:padding="@dimen/notification_2025_icon_circle_padding"
android:maxDrawableWidth="@dimen/notification_2025_icon_circle_size"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index bfa3b06..c02c13c 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Beller-ID se verstek is beperk. Volgende oproep: nie beperk nie"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Beller-ID se verstek is nie beperk nie. Volgende oproep: beperk"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Beller-ID se verstek is nie beperk nie. Volgende oproep: nie beperk nie"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Hierdie app is nie versoenbaar met 16 KB nie. APK-belyningkontrole het misluk. Hierdie app sal met bladsygrootteversoenbare modus gebruik word. Stel asseblief weer die app met 16 KB-ondersteuning saam vir beste versoenbaarheid. Sien <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> vir meer inligting"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Hierdie app is nie versoenbaar met 16 KB nie. ELF-belyningkontrole het misluk. Hierdie app sal met bladsygrootteversoenbare modus gebruik word. Stel asseblief weer die app met 16 KB-ondersteuning saam vir beste versoenbaarheid. Sien <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> vir meer inligting"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Hierdie app is nie versoenbaar met 16 KB nie. APK- ELF-belyningkontroles het misluk. Hierdie app sal met bladsygrootteversoenbare modus gebruik word. Stel asseblief weer die app met 16 KB-ondersteuning saam vir beste versoenbaarheid. Sien <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> vir meer inligting"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Diens nie verskaf nie."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Jy kan nie die beller-ID-instelling verander nie."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Het data oorgeskakel na <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -308,7 +305,7 @@
<string name="notification_channel_alerts" msgid="5070241039583668427">"Opletberigte"</string>
<string name="notification_channel_retail_mode" msgid="3732239154256431213">"Kleinhandeldemonstrasie"</string>
<string name="notification_channel_usb" msgid="1528280969406244896">"USB-verbinding"</string>
- <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Program loop tans"</string>
+ <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App loop tans"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Programme wat batterykrag gebruik"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Vergroting"</string>
<string name="notification_channel_accessibility_security_policy" msgid="1727787021725251912">"Toeganklikheidgebruik"</string>
@@ -372,41 +369,41 @@
<string name="permlab_statusBar" msgid="8798267849526214017">"deaktiveer of verander statusbalk"</string>
<string name="permdesc_statusBar" msgid="5809162768651019642">"Laat die app toe om die statusbalk te deaktiveer en stelselikone by te voeg of te verwyder."</string>
<string name="permlab_statusBarService" msgid="2523421018081437981">"wees die statusbalk"</string>
- <string name="permdesc_statusBarService" msgid="6652917399085712557">"Laat die program toe om die statusbalk te wees."</string>
+ <string name="permdesc_statusBarService" msgid="6652917399085712557">"Laat die app toe om die statusbalk te wees."</string>
<string name="permlab_expandStatusBar" msgid="1184232794782141698">"vou statusbalk in of uit"</string>
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Laat die program toe om die statusbalk uit te vou of in te vou."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"wys kennisgewings as volskermaktiwiteite op \'n geslote skerm"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Laat die program toe om kennisgewings as volskermaktiwiteite op \'n geslote toestel te wys"</string>
<string name="permlab_install_shortcut" msgid="7451554307502256221">"installeer kortpaaie"</string>
- <string name="permdesc_install_shortcut" msgid="4476328467240212503">"Stel \'n program in staat om Tuisskerm-kortpaaie by te voeg sonder gebruikerinmenging."</string>
+ <string name="permdesc_install_shortcut" msgid="4476328467240212503">"Stel \'n app in staat om Tuisskerm-kortpaaie by te voeg sonder gebruikerinmenging."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"deïnstalleer kortpaaie"</string>
- <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Laat die program toe om Tuisskerm-kortpaaie te verwyder sonder gebruikerinmenging."</string>
+ <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Laat die app toe om Tuisskerm-kortpaaie te verwyder sonder gebruikerinmenging."</string>
<string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"herlei uitgaande oproepe"</string>
<string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"Laat die program toe om te sien watter nommer tydens \'n uitgaande oproep geskakel word, met die opsie om die oproep na \'n ander nommer te herlei of die oproep heeltemal te beëindig."</string>
<string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"antwoord foonoproepe"</string>
<string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"Laat die program toe om inkomende foonoproepe te antwoord."</string>
<string name="permlab_receiveSms" msgid="505961632050451881">"ontvang teksboodskappe (SMS)"</string>
- <string name="permdesc_receiveSms" msgid="1797345626687832285">"Laat die program toe om SMS-boodskappe te ontvang en te verwerk. Dit beteken dat die program boodskappe wat na jou toestel gestuur is, kan monitor of uitvee, sonder dat jy dit gesien het."</string>
+ <string name="permdesc_receiveSms" msgid="1797345626687832285">"Laat die app toe om SMS-boodskappe te ontvang en te verwerk. Dit beteken dat die app boodskappe wat na jou toestel gestuur is, kan monitor of uitvee, sonder dat jy dit gesien het."</string>
<string name="permlab_receiveMms" msgid="4000650116674380275">"ontvang teksboodskappe (MMS)"</string>
<string name="permdesc_receiveMms" msgid="958102423732219710">"Laat die program toe om MMS-boodskappe te ontvang en te verwerk. Dit beteken dat die program boodskappe wat na jou toestel gestuur is kan monitor of uitvee, sonder dat jy dit gesien het."</string>
<string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"Stuur seluitsendingboodskappe aan"</string>
- <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Laat die program toe om die seluitsendingmodule te bind om seluitsendingboodskappe aan te stuur wanneer hulle ontvang word. Seluitsendingwaarskuwings word in sommige liggings gelewer om jou oor noodsituasies te waarsku. Kwaadwillige programme kan met die werkverrigting of werking van jou toestel inmeng wanneer \'n noodseluitsending ontvang word."</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Laat die app toe om aan die seluitsendingmodule te bind om seluitsendingboodskappe aan te stuur wanneer hulle ontvang word. Seluitsendingwaarskuwings word in sommige liggings gelewer om jou oor noodsituasies te waarsku. Kwaadwillige apps kan met die werkverrigting of werking van jou toestel inmeng wanneer \'n noodseluitsending ontvang word."</string>
<string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Bestuur voortgaande oproepe"</string>
<string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Stel \'n program in staat om besonderhede oor voortgaande oproepe op jou toestel te sien, en hierdie oproepe te beheer."</string>
<string name="permlab_accessLastKnownCellId" msgid="7638226620825665130">"Kry toegang tot laaste bekende selidentiteit."</string>
<string name="permdesc_accessLastKnownCellId" msgid="6664621339249308857">"Laat ’n app toe om toegang tot die laaste bekende selidentiteit te kry wat deur telefonie verskaf is"</string>
<string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"lees seluitsending-boodskappe"</string>
- <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Laat die program toe om seluitsending-boodskappe te lees wat deur jou toestel ontvang word. Seluitsending-waarskuwings word in sommige plekke afgelewer om jou van noodsituasies te waarsku. Kwaadwillige programme mag inmeng met die prestasie of die werking van jou toestel wanneer \'n noodgeval se seluitsending ontvang word."</string>
+ <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Laat die app toe om seluitsending-boodskappe te lees wat deur jou toestel ontvang word. Seluitsending-waarskuwings word in sommige plekke afgelewer om jou van noodsituasies te waarsku. Kwaadwillige apps kan inmeng met die prestasie of die werking van jou toestel wanneer \'n noodgeval se seluitsending ontvang word."</string>
<string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"lees ingetekende nuus"</string>
- <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"Laat die program toe om details oor die tans gesinkroniseerde strome te kry."</string>
+ <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"Laat die app toe om details oor die tans gesinkroniseerde strome te kry."</string>
<string name="permlab_sendSms" msgid="7757368721742014252">"SMS-boodskappe te stuur en te bekyk"</string>
- <string name="permdesc_sendSms" msgid="6757089798435130769">"Laat die program toe om SMS-boodskappe te stuur. Dit kan tot onverwagse heffings lei. Kwaadwillige programme kan jou geld kos deur boodskappe sonder jou bevestiging te stuur."</string>
+ <string name="permdesc_sendSms" msgid="6757089798435130769">"Laat die app toe om SMS-boodskappe te stuur. Dit kan tot onverwagse heffings lei. Kwaadwillige apps kan jou geld kos deur boodskappe sonder jou bevestiging te stuur."</string>
<string name="permlab_readSms" msgid="5164176626258800297">"lees jou teksboodskappe (SMS of MMS)"</string>
<string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"Hierdie program kan alle SMS\'e (teksboodskappe) wat op jou tablet geberg is, lees."</string>
- <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"Hierdie program kan alle SMS- (teks)-boodskappe lees wat op jou Android TV-toestel geberg is."</string>
+ <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"Hierdie app kan alle SMS- (teks)-boodskappe lees wat op jou Android TV-toestel geberg is."</string>
<string name="permdesc_readSms" product="default" msgid="774753371111699782">"Hierdie program kan alle SMS\'e (teksboodskappe) wat op jou foon geberg is, lees."</string>
<string name="permlab_receiveWapPush" msgid="4223747702856929056">"ontvang teksboodskappe (WAP)"</string>
- <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"Laat die program toe om WAP-boodskappe te ontvang en te verwerk. Hierdie toestemming sluit ook in dat boodskappe wat na jou toestel gestuur is, gemonitor of uitgevee kan word, sonder dat jy dit gesien het."</string>
+ <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"Laat die app toe om WAP-boodskappe te ontvang en te verwerk. Hierdie toestemming sluit ook in dat boodskappe wat na jou toestel gestuur is, gemonitor of uitgevee kan word, sonder dat jy dit gesien het."</string>
<string name="permlab_getTasks" msgid="7460048811831750262">"haal lopende programme op"</string>
<string name="permdesc_getTasks" msgid="7388138607018233726">"Laat die program toe om inligting oor die huidig- en onlangslopende take op te haal. Dit kan moontlik die program toelaat om inligting oor watter programme op die toestel gebruik word, te ontdek."</string>
<string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"bestuur profiel- en toesteleienaars"</string>
@@ -414,25 +411,25 @@
<string name="permlab_reorderTasks" msgid="7598562301992923804">"herrangskik lopende programme"</string>
<string name="permdesc_reorderTasks" msgid="8796089937352344183">"Laat die program toe om take na die voorgrond of agtergrond te skuif. Die program kan dit moontlik sonder jou insette doen."</string>
<string name="permlab_enableCarMode" msgid="893019409519325311">"aktiveer motormodus"</string>
- <string name="permdesc_enableCarMode" msgid="56419168820473508">"Laat die program toe om die motormodus te aktiveer."</string>
+ <string name="permdesc_enableCarMode" msgid="56419168820473508">"Laat die app toe om die motormodus te aktiveer."</string>
<string name="permlab_killBackgroundProcesses" msgid="6559320515561928348">"maak ander programme toe"</string>
- <string name="permdesc_killBackgroundProcesses" msgid="2357013583055434685">"Laat die program toe om agtergrondprosesse van ander programme te beëindig. Dit kan moontlik veroorsaak dat ander programme ophou werk."</string>
- <string name="permlab_systemAlertWindow" msgid="5757218350944719065">"Hierdie program kan bo-op ander programme verskyn"</string>
- <string name="permdesc_systemAlertWindow" msgid="1145660714855738308">"Hierdie program kan bokant ander programme of ander dele van die skerm verskyn. Dit kan met normale programgebruik inmeng en die voorkoms van ander programme verander."</string>
+ <string name="permdesc_killBackgroundProcesses" msgid="2357013583055434685">"Laat die app toe om agtergrondprosesse van ander apps te beëindig. Dit kan moontlik veroorsaak dat ander apps ophou werk."</string>
+ <string name="permlab_systemAlertWindow" msgid="5757218350944719065">"Hierdie app kan bo-op ander apps verskyn"</string>
+ <string name="permdesc_systemAlertWindow" msgid="1145660714855738308">"Hierdie app kan bokant ander apps of ander dele van die skerm verskyn. Dit kan met normale appgebruik inmeng en die voorkoms van ander apps verander."</string>
<string name="permlab_hideOverlayWindows" msgid="6382697828482271802">"versteek ander apps se oorleggers"</string>
<string name="permdesc_hideOverlayWindows" msgid="5660242821651958225">"Hierdie app kan versoek dat die stelsel oorleggers wat oorspronklik van apps af kom, versteek sodat hulle nie bo-op hulle wys nie."</string>
<string name="permlab_runInBackground" msgid="541863968571682785">"loop op die agtergrond"</string>
- <string name="permdesc_runInBackground" msgid="4344539472115495141">"Hierdie program kan op die agtergrond loop. Dit kan die battery vinniger laat pap word."</string>
+ <string name="permdesc_runInBackground" msgid="4344539472115495141">"Hierdie app kan op die agtergrond loop. Dit kan die battery vinniger laat pap word."</string>
<string name="permlab_useDataInBackground" msgid="783415807623038947">"gebruik data op die agtergrond"</string>
- <string name="permdesc_useDataInBackground" msgid="1230753883865891987">"Hierdie program kan data op die agtergrond gebruik. Dit kan die datagebruik vergroot."</string>
+ <string name="permdesc_useDataInBackground" msgid="1230753883865891987">"Hierdie app kan data op die agtergrond gebruik. Dit kan die datagebruik vergroot."</string>
<string name="permlab_schedule_exact_alarm" msgid="6683283918033029730">"Skeduleer handelinge met presiese tydsbesturing"</string>
<string name="permdesc_schedule_exact_alarm" msgid="8198009212013211497">"Hierdie app kan werk skeduleer om op ’n gewenste tyd in die toekoms plaas te vind. Dit beteken ook dat die app kan werk wanneer die toestel nie aktief gebruik word nie."</string>
<string name="permlab_use_exact_alarm" msgid="348045139777131552">"Skeduleer wekkers of geleentheidonthounotas"</string>
<string name="permdesc_use_exact_alarm" msgid="7033761461886938912">"Hierdie app kan handelinge soos wekkers en onthounotas skeduleer om jou op ’n gewenste tyd in die toekoms in kennis te stel."</string>
- <string name="permlab_persistentActivity" msgid="464970041740567970">"laat program altyd loop"</string>
- <string name="permdesc_persistentActivity" product="tablet" msgid="6055271149187369916">"Laat die program toe om dele van ditself deurdringend in die geheue te hou. Dit kan geheue wat aan ander programme beskikbaar is, beperk, en die tablet stadiger maak."</string>
- <string name="permdesc_persistentActivity" product="tv" msgid="6800526387664131321">"Laat die program toe om dele daarvan in die geheue te laat voortbestaan. Dit kan geheue wat vir ander programme beskikbaar is, beperk en sodoende jou Android TV-toestel stadiger maak."</string>
- <string name="permdesc_persistentActivity" product="default" msgid="1914841924366562051">"Laat die program toe om dele van ditself deurdringend in die geheue te hou. Dit kan geheue wat aan ander programme beskikbaar is, beperk, en die foon stadiger maak."</string>
+ <string name="permlab_persistentActivity" msgid="464970041740567970">"laat app altyd loop"</string>
+ <string name="permdesc_persistentActivity" product="tablet" msgid="6055271149187369916">"Laat die app toe om dele van die app deurdringend in die geheue te hou. Dit kan geheue wat aan ander apps beskikbaar is, beperk, en die tablet stadiger maak."</string>
+ <string name="permdesc_persistentActivity" product="tv" msgid="6800526387664131321">"Laat die app toe om dele daarvan in die geheue te laat voortbestaan. Dit kan geheue wat vir ander apps beskikbaar is, beperk en sodoende jou Android TV-toestel stadiger maak."</string>
+ <string name="permdesc_persistentActivity" product="default" msgid="1914841924366562051">"Laat die app toe om dele van die app deurdringend in die geheue te hou. Dit kan geheue wat aan ander apps beskikbaar is, beperk, en die foon stadiger maak."</string>
<string name="permlab_foregroundService" msgid="1768855976818467491">"laat loop voorgronddiens"</string>
<string name="permdesc_foregroundService" msgid="8720071450020922795">"Laat die program toe om van voorgronddienste gebruik te maak."</string>
<string name="permlab_foregroundServiceCamera" msgid="7814751737955715297">"gebruik voorgronddienstipe “kamera”"</string>
@@ -468,13 +465,13 @@
<string name="permlab_writeSettings" msgid="8057285063719277394">"verander stelsel-instellings"</string>
<string name="permdesc_writeSettings" msgid="8293047411196067188">"Laat die program toe om die stelsel se instellingsdata te verander. Kwaadwillige programme kan dalk jou stelsel se opstelling korrupteer."</string>
<string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"laat loop wanneer begin"</string>
- <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"Laat die program toe om homself te begin so gou as moontlik nadat die stelsel laai. Dit maak dat dit langer neem vir die tablet om te begin, en dit laat die foon toe om die tablet stadiger te maak omdat dit altyd loop."</string>
- <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"Laat die program toe om self te begin sodra die stelsel klaar geselflaai het. Dit kan dalk daartoe lei dat die toestel langer neem om jou Android TV-toestel te begin, en laat die program toe om die hele toestel stadiger te maak deur altyd te loop."</string>
+ <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"Laat die app toe om self te begin sodra die stelsel geselflaai het. Dit maak dat dit langer neem vir die tablet om te begin, en dit laat die foon toe om die tablet stadiger te maak omdat dit altyd loop."</string>
+ <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"Laat die app toe om self te begin sodra die stelsel klaar geselflaai het. Dit kan dalk daartoe lei dat die toestel langer neem om jou Android TV-toestel te begin, en laat die app toe om die hele toestel stadiger te maak deur altyd te loop."</string>
<string name="permdesc_receiveBootCompleted" product="default" msgid="7912677044558690092">"Laat die program toe om homself te begin so gou as moontlik nadat die stelsel laai. Dit maak dat dit langer neem vir die foon om te begin, en dit laat die foon toe om die foon stadiger te maak omdat dit altyd loop."</string>
<string name="permlab_broadcastSticky" msgid="4552241916400572230">"Stuur klewerige uitsending"</string>
- <string name="permdesc_broadcastSticky" product="tablet" msgid="5058486069846384013">"Laat die program toe om taai uitsendings te stuur, wat agterbly nadat die uitsending klaar is. Oormatige gebruik kan die tablet stadig of onstabiel maak deurdat dit te veel geheue gebruik."</string>
- <string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"Laat die program toe om vaste uitsendings wat agterbly nadat die uitsending eindig, te stuur. Oormatige gebruik kan jou Android TV-toestel stadig of onstabiel maak omdat dit veroorsaak dat jou toestel te veel geheue gebruik."</string>
- <string name="permdesc_broadcastSticky" product="default" msgid="134529339678913453">"Laat die program toe om taai uitsendings te stuur, wat agterbly nadat die uitsending klaar is. Oormatige gebruik kan die foon stadig of onstabiel maak deurdat dit te veel geheue gebruik."</string>
+ <string name="permdesc_broadcastSticky" product="tablet" msgid="5058486069846384013">"Laat die app toe om vaste uitsendings te stuur, wat agterbly nadat die uitsending klaar is. Oormatige gebruik kan die tablet stadig of onstabiel maak deurdat dit te veel geheue gebruik."</string>
+ <string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"Laat die app toe om vaste uitsendings wat agterbly nadat die uitsending eindig, te stuur. Oormatige gebruik kan jou Android TV-toestel stadig of onstabiel maak omdat dit veroorsaak dat jou toestel te veel geheue gebruik."</string>
+ <string name="permdesc_broadcastSticky" product="default" msgid="134529339678913453">"Laat die app toe om vaste uitsendings te stuur, wat agterbly nadat die uitsending klaar is. Oormatige gebruik kan die foon stadig of onstabiel maak deurdat dit te veel geheue gebruik."</string>
<string name="permlab_readContacts" msgid="8776395111787429099">"lees jou kontakte"</string>
<string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"Laat die program toe om data te lees oor jou kontakte wat op jou tablet geberg is. Programme sal ook toegang hê tot die rekeninge op jou tablet wat kontakte geskep het. Dit kan rekeninge insluit wat geskep is deur programme wat jy geïnstalleer het. Hierdie toestemming laat programme toe om jou kontakdata te stoor, en kwaadwillige programme kan kontakdata deel sonder dat jy dit weet."</string>
<string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"Laat die program toe om data te lees oor jou kontakte wat op jou Android TV-toestel geberg is. Programme sal ook toegang hê tot die rekeninge op jou Android TV-toestel wat kontakte geskep het. Dit kan rekeninge insluit wat geskep is deur programme wat jy geïnstalleer het. Hierdie toestemming laat programme toe om jou kontakdata te stoor, en kwaadwillige programme kan kontakdata deel sonder dat jy dit weet."</string>
@@ -486,23 +483,23 @@
<string name="permlab_readCallLog" msgid="1739990210293505948">"lees oproeprekord"</string>
<string name="permdesc_readCallLog" msgid="8964770895425873433">"Hierdie program kan jou oproepgeskiedenis lees."</string>
<string name="permlab_writeCallLog" msgid="670292975137658895">"skryf oproeprekord"</string>
- <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"Laat die program toe om jou tablet se oproeprekord, insluitende data oor inkomende en uitgaande oproepe, te verander. Kwaadwillige programme kan dit gebruik om jou oproeprekord uit te vee of te verander."</string>
- <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Laat die program toe om jou Android TV-toestel se oproeprekord te wysig, insluitend data oor inkomende en uitgaande oproepe. Kwaadwillige programme kan dit gebruik om jou oproeprekord uit te vee of te wysig."</string>
- <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Laat die program toe om jou foon se oproeprekord, insluitende data oor inkomende en uitgaande oproepe, te verander. Kwaadwillige programme kan dit gebruik om jou oproeprekord uit te vee of te verander."</string>
- <string name="permlab_bodySensors" msgid="662918578601619569">"Kry toegang tot liggaamsensordata, soos polsslag, terwyl program gebruik word"</string>
+ <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"Laat die app toe om jou tablet se oproeprekord, insluitende data oor inkomende en uitgaande oproepe, te verander. Kwaadwillige apps kan dit gebruik om jou oproeprekord uit te vee of te verander."</string>
+ <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Laat die app toe om jou Android TV-toestel se oproeprekord te wysig, insluitend data oor inkomende en uitgaande oproepe. Kwaadwillige apps kan dit gebruik om jou oproeprekord uit te vee of te wysig."</string>
+ <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Laat die app toe om jou foon se oproeprekord, insluitende data oor inkomende en uitgaande oproepe, te verander. Kwaadwillige apps kan dit gebruik om jou oproeprekord uit te vee of te verander."</string>
+ <string name="permlab_bodySensors" msgid="662918578601619569">"Kry toegang tot liggaamsensordata, soos polsslag, terwyl app gebruik word"</string>
<string name="permdesc_bodySensors" product="default" msgid="7652650410295512140">"Gee die app toegang tot liggaamsensordata, soos polsslag, temperatuur en bloedsuurstofpersentasie, terwyl die app gebruik word."</string>
- <string name="permlab_bodySensors_background" msgid="4912560779957760446">"Kry toegang tot liggaamsensordata, soos polsslag, terwyl program op agtergrond is"</string>
- <string name="permdesc_bodySensors_background" product="default" msgid="8870726027557749417">"Gee die program toegang tot liggaamsensordata, soos polsslag, temperatuur en bloedsuurstofpersentasie, terwyl dit op die agtergrond is."</string>
+ <string name="permlab_bodySensors_background" msgid="4912560779957760446">"Kry toegang tot liggaamsensordata, soos polsslag, terwyl app op die agtergrond is"</string>
+ <string name="permdesc_bodySensors_background" product="default" msgid="8870726027557749417">"Gee die app toegang tot liggaamsensordata, soos polsslag, temperatuur en bloedsuurstofpersentasie, terwyl dit op die agtergrond is."</string>
<string name="permlab_readCalendar" msgid="6408654259475396200">"Lees kalendergebeurtenisse en -besonderhede"</string>
- <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Hierdie program kan alle kalendergebeurtenisse lees wat op jou tablet geberg is of jou kalenderdata stoor."</string>
- <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Hierdie program kan alle kalendergeleenthede wat op jou Android TV-toestel geberg is, lees of jou kalenderdata stoor."</string>
+ <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Hierdie app kan alle kalendergebeurtenisse lees wat op jou tablet geberg is of jou kalenderdata stoor."</string>
+ <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Hierdie app kan alle kalendergeleenthede lees wat op jou Android TV-toestel geberg is of jou kalenderdata stoor."</string>
<string name="permdesc_readCalendar" product="default" msgid="9118823807655829957">"Hierdie program kan alle kalendergebeurtenisse lees wat op jou foon geberg is of jou kalenderdata stoor."</string>
<string name="permlab_writeCalendar" msgid="6422137308329578076">"voeg by of verander kalenderafsprake en stuur \'n e-pos aan gaste sonder eienaars se medewete"</string>
<string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"Hierdie program kan kalendergebeurtenisse op jou tablet byvoeg, verwyder of verander. Hierdie program kan boodskappe stuur wat lyk of dit van kalendereienaars af kom of gebeurtenisse verander sonder om hul eienaars in te lig."</string>
<string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"Hierdie program kan kalendergeleenthede op jou Android TV-toestel byvoeg, verwyder of verander. Hierdie program kan boodskappe stuur wat lyk of dit van kalendereienaars af kom of geleenthede verander sonder om hul eienaars in kennis te stel."</string>
- <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"Hierdie program kan kalendergebeurtenisse op jou foon byvoeg, verwyder of verander. Hierdie program kan boodskappe stuur wat lyk of dit van kalendereienaars af kom of gebeurtenisse verander sonder om hul eienaars in te lig."</string>
+ <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"Hierdie app kan kalendergebeurtenisse op jou foon byvoeg, verwyder of verander. Hierdie app kan boodskappe stuur wat lyk of dit van kalendereienaars af kom of gebeurtenisse verander sonder om hul eienaars in te lig."</string>
<string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"Kry toegang tot ekstra liggingverskaffer-bevele"</string>
- <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Gee die program toegang tot ekstra liggingverskaffer-bevele. Dit kan die program dalk toelaat om in te meng met die werking van die GPS of ander liggingbronne."</string>
+ <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Gee die app toegang tot ekstra liggingverskaffer-bevele. Dit kan die app dalk toelaat om in te meng met die werking van die GPS of ander liggingbronne."</string>
<string name="permlab_accessFineLocation" msgid="6426318438195622966">"kry net op die voorgrond toegang tot presiese ligging"</string>
<string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Hierdie program kan jou presiese ligging van liggingdienste af kry terwyl die program gebruik word. Liggingdienste vir jou toestel moet aangeskakel wees vir die program om die ligging te kry. Dit kan batterygebruik verhoog."</string>
<string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"kry benaderde ligging net op die voorgrond"</string>
@@ -518,14 +515,14 @@
<string name="permlab_detectScreenCapture" msgid="4447042362828799433">"bespeur skermskote van appvensters"</string>
<string name="permdesc_detectScreenCapture" msgid="3485784917960342284">"Hierdie app sal ingelig word as ’n skermskoot geneem word terwyl die app gebruik word."</string>
<string name="permlab_sim_communication" msgid="176788115994050692">"stuur bevele na die SIM"</string>
- <string name="permdesc_sim_communication" msgid="4179799296415957960">"Laat die program toe om bevele na die SIM te stuur. Dit is baie gevaarlik."</string>
+ <string name="permdesc_sim_communication" msgid="4179799296415957960">"Laat die app toe om bevele na die SIM te stuur. Dit is baie gevaarlik."</string>
<string name="permlab_activityRecognition" msgid="1782303296053990884">"herken fisieke aktiwiteit"</string>
<string name="permdesc_activityRecognition" msgid="8667484762991357519">"Hierdie program kan jou fisieke aktiwiteit herken."</string>
<string name="permlab_camera" msgid="6320282492904119413">"neem foto\'s en video\'s"</string>
<string name="permdesc_camera" msgid="5240801376168647151">"Hierdie program kan met die kamera foto\'s neem en video\'s opneem terwyl die program gebruik word."</string>
<string name="permlab_backgroundCamera" msgid="7549917926079731681">"neem foto\'s en video\'s op die agtergrond"</string>
<string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Hierdie program kan enige tyd met die kamera foto\'s neem en video\'s opneem."</string>
- <string name="permlab_systemCamera" msgid="3642917457796210580">"Gee \'n program of diens toegang tot stelselkameras om foto\'s en video\'s te neem"</string>
+ <string name="permlab_systemCamera" msgid="3642917457796210580">"Gee \'n app of diens toegang tot stelselkameras om foto\'s en video\'s te neem"</string>
<string name="permdesc_systemCamera" msgid="5938360914419175986">"Hierdie bevoorregte of stelselprogram kan enige tyd met \'n stelselkamera foto\'s neem en video\'s opneem. Vereis dat die program ook die android.permission.CAMERA-toestemming het"</string>
<string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Laat \'n program of diens toe om terugbeloproepe te ontvang oor kameratoestelle wat oopgemaak of toegemaak word."</string>
<string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Hierdie program kan terugbeloproepe ontvang wanneer enige kameratoestel oopgemaak (deur watter program) of toegemaak word."</string>
@@ -537,19 +534,19 @@
<string name="permlab_callPhone" msgid="1798582257194643320">"skakel foonnommers direk"</string>
<string name="permdesc_callPhone" msgid="7892422187827695656">"Laat die app toe om foonnommers sonder jou insae te bel. Dit kan onvoorsiene heffings of oproepe tot gevolg hê. Neem kennis dat dit nie die app toelaat om noodnommers te bel nie. Kwaadwillige apps kan jou geld kos deur oproepe te maak sonder jou bevestiging of diensverskafferkodes te bel wat veroorsaak dat inkomende oproepe outomaties na ’n ander nommer aangestuur word."</string>
<string name="permlab_accessImsCallService" msgid="442192920714863782">"toegang tot kitsboodskapoproepdiens"</string>
- <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Laat die program toe om die kitsboodskapdiens te gebruik om oproepe sonder jou ingryping te maak."</string>
+ <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Laat die app toe om die kitsboodskapdiens te gebruik om oproepe sonder jou ingryping te maak."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"lees foonstatus en identiteit"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Laat die program toe om toegang tot die foonfunksies van die toestel te verkry. Hierdie toestemming laat die program toe om te bepaal wat die foonnommer en toestel-IDs is, of die oproep aan die gang is, en die afgeleë nommer wat deur \'n oproep verbind word."</string>
<string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"lees basiese telefoniestatus en -identiteit"</string>
<string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Gee die program toegang tot die toestel se basiese telefoniekenmerke."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"roeteer oproepe deur die stelsel"</string>
- <string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Laat die program toe om sy oproepe deur die stelsel te stuur om die oproepervaring te verbeter."</string>
+ <string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Laat die app toe om sy oproepe deur die stelsel te stuur om die oproepervaring te verbeter."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"sien en beheer oproepe deur die stelsel."</string>
<string name="permdesc_callCompanionApp" msgid="8474168926184156261">"Laat die program toe om deurlopende oproepe op die toestel te sien en te beheer. Dit sluit inligting in soos oproepnommers vir oproepe en die toedrag van die oproepe."</string>
<string name="permlab_exemptFromAudioRecordRestrictions" msgid="1164725468350759486">"vrygestel van beperkings op oudio-opnames"</string>
<string name="permdesc_exemptFromAudioRecordRestrictions" msgid="2425117015896871976">"Stel die program vry van beperkings om oudio op te neem."</string>
- <string name="permlab_acceptHandover" msgid="2925523073573116523">"gaan voort met \'n oproep uit \'n ander program"</string>
- <string name="permdesc_acceptHandovers" msgid="7129026180128626870">"Laat die program toe om \'n oproep voort te sit wat in \'n ander program begin is."</string>
+ <string name="permlab_acceptHandover" msgid="2925523073573116523">"gaan voort met \'n oproep uit \'n ander app"</string>
+ <string name="permdesc_acceptHandovers" msgid="7129026180128626870">"Laat die app toe om \'n oproep voort te sit wat in \'n ander app begin is."</string>
<string name="permlab_readPhoneNumbers" msgid="5668704794723365628">"lees foonnommers"</string>
<string name="permdesc_readPhoneNumbers" msgid="7368652482818338871">"Laat die program toe om toegang tot die toestel se foonnommers te kry."</string>
<string name="permlab_wakeLock" product="automotive" msgid="1904736682319375676">"hou motorskerm aan"</string>
@@ -557,57 +554,57 @@
<string name="permlab_wakeLock" product="tv" msgid="2856941418123343518">"keer dat jou Android TV-toestel slaap"</string>
<string name="permlab_wakeLock" product="default" msgid="569409726861695115">"verhoed foon om te slaap"</string>
<string name="permdesc_wakeLock" product="automotive" msgid="5995045369683254571">"Laat die program toe om die motorskerm aan te hou."</string>
- <string name="permdesc_wakeLock" product="tablet" msgid="2441742939101526277">"Laat die program toe om die tablet te keer om te slaap."</string>
- <string name="permdesc_wakeLock" product="tv" msgid="2329298966735118796">"Laat die program toe om te keer dat jou Android TV-toestel slaap."</string>
- <string name="permdesc_wakeLock" product="default" msgid="3689523792074007163">"Laat die program toe om die foon te keer om te slaap."</string>
+ <string name="permdesc_wakeLock" product="tablet" msgid="2441742939101526277">"Laat die app toe om die tablet te keer om te slaap."</string>
+ <string name="permdesc_wakeLock" product="tv" msgid="2329298966735118796">"Laat die app toe om te keer dat jou Android TV-toestel slaap."</string>
+ <string name="permdesc_wakeLock" product="default" msgid="3689523792074007163">"Laat die app toe om die foon te keer om te slaap."</string>
<string name="permlab_transmitIr" msgid="8077196086358004010">"versend infrarooi"</string>
- <string name="permdesc_transmitIr" product="tablet" msgid="5884738958581810253">"Laat die program toe om die tablet se infrarooisender te gebruik."</string>
- <string name="permdesc_transmitIr" product="tv" msgid="3278506969529173281">"Laat die program toe om jou Android TV-toestel se infrarooisender te gebruik."</string>
+ <string name="permdesc_transmitIr" product="tablet" msgid="5884738958581810253">"Laat die app toe om die tablet se infrarooisender te gebruik."</string>
+ <string name="permdesc_transmitIr" product="tv" msgid="3278506969529173281">"Laat die app toe om jou Android TV-toestel se infrarooisender te gebruik."</string>
<string name="permdesc_transmitIr" product="default" msgid="8484193849295581808">"Laat die program toe om die foon se infrarooisender te gebruik."</string>
<string name="permlab_setWallpaper" msgid="6959514622698794511">"stel muurpapier"</string>
- <string name="permdesc_setWallpaper" msgid="2973996714129021397">"Laat die program toe om die stelsel se muurpapier te stel."</string>
+ <string name="permdesc_setWallpaper" msgid="2973996714129021397">"Laat die app toe om die stelsel se muurpapier te stel."</string>
<string name="permlab_accessHiddenProfile" msgid="8607094418491556823">"Kry toegang tot versteekte profiele"</string>
<string name="permdesc_accessHiddenProfile" msgid="1543153202481009676">"Laat die app toe om toegang tot versteekte profiele te kry."</string>
<string name="permlab_setWallpaperHints" msgid="1153485176642032714">"verstel jou muurpapier se grootte"</string>
- <string name="permdesc_setWallpaperHints" msgid="6257053376990044668">"Laat die program toe om die stelsel se muurpapier se groottewenke te stel."</string>
+ <string name="permdesc_setWallpaperHints" msgid="6257053376990044668">"Laat die app toe om die stelsel se muurpapier se groottewenke te stel."</string>
<string name="permlab_setTimeZone" msgid="7922618798611542432">"stel tydsone"</string>
- <string name="permdesc_setTimeZone" product="tablet" msgid="1788868809638682503">"Laat die program toe om die tablet se tydsone te verander."</string>
+ <string name="permdesc_setTimeZone" product="tablet" msgid="1788868809638682503">"Laat die app toe om die tablet se tydsone te verander."</string>
<string name="permdesc_setTimeZone" product="tv" msgid="9069045914174455938">"Laat die program toe om jou Android TV-toestel se tydsone te verander."</string>
- <string name="permdesc_setTimeZone" product="default" msgid="4611828585759488256">"Laat die program toe om die foon se tydsone te verander."</string>
+ <string name="permdesc_setTimeZone" product="default" msgid="4611828585759488256">"Laat die app toe om die foon se tydsone te verander."</string>
<string name="permlab_getAccounts" msgid="5304317160463582791">"soek rekeninge op die toestel"</string>
- <string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"Laat die program toe om die lys van rekeninge wat aan die tablet bekend is, te kry. Dit kan moontlik enige rekeninge wat geskep is deur programme wat jy geïnstalleer het, insluit."</string>
- <string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"Laat die program toe om die lys rekeninge wat aan jou Android TV-toestel bekend is, te kry. Dit kan dalk rekeninge insluit wat geskep is deur programme wat jy geïnstalleer het."</string>
- <string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"Laat die program toe om die lys van rekeninge wat aan die foon bekend is, te kry. Dit kan moontlik enige rekeninge wat geskep is deur programme wat jy geïnstalleer het, insluit."</string>
+ <string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"Laat die app toe om die lys van rekeninge wat aan die tablet bekend is, te kry. Dit kan moontlik enige rekeninge wat geskep is deur apps wat jy geïnstalleer het, insluit."</string>
+ <string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"Laat die app toe om die lys rekeninge wat aan jou Android TV-toestel bekend is, te kry. Dit kan dalk rekeninge insluit wat geskep is deur apps wat jy geïnstalleer het."</string>
+ <string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"Laat die app toe om die lys van rekeninge wat aan die foon bekend is, te kry. Dit kan moontlik enige rekeninge wat geskep is deur apps wat jy geïnstalleer het, insluit."</string>
<string name="permlab_accessNetworkState" msgid="2349126720783633918">"bekyk netwerkverbindings"</string>
- <string name="permdesc_accessNetworkState" msgid="4394564702881662849">"Laat die program toe om inligting oor netwerkverbindings, soos watter netwerke bestaan en gekoppel is, te sien."</string>
+ <string name="permdesc_accessNetworkState" msgid="4394564702881662849">"Laat die app toe om inligting oor netwerkverbindings, soos watter netwerke bestaan en gekoppel is, te sien."</string>
<string name="permlab_createNetworkSockets" msgid="3224420491603590541">"verkry volle netwerktoegang"</string>
<string name="permdesc_createNetworkSockets" msgid="7722020828749535988">"Laat die program toe om netwerksokke te skep en gepasmaakte netwerkprotokolle te gebruik. Die blaaier en ander programme verskaf reeds die middele waardeur data na die internet gestuur kan word, so hierdie toestemming word nie vereis om data na die internet te stuur nie."</string>
<string name="permlab_changeNetworkState" msgid="8945711637530425586">"verander netwerkverbinding"</string>
- <string name="permdesc_changeNetworkState" msgid="649341947816898736">"Laat die program toe om die status van netwerkkonnektiwiteit te verander."</string>
+ <string name="permdesc_changeNetworkState" msgid="649341947816898736">"Laat die app toe om die status van netwerkkonnektiwiteit te verander."</string>
<string name="permlab_changeTetherState" msgid="9079611809931863861">"verander verbinde konnektiwiteit"</string>
- <string name="permdesc_changeTetherState" msgid="3025129606422533085">"Laat die program toe om die status van verbinde netwerkkonnektiwiteit te verander."</string>
+ <string name="permdesc_changeTetherState" msgid="3025129606422533085">"Laat die app toe om die status van verbinde netwerkkonnektiwiteit te verander."</string>
<string name="permlab_accessWifiState" msgid="5552488500317911052">"bekyk Wi-Fi-verbindings"</string>
- <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Laat die program toe om inligting oor Wi-Fi-netwerke, soos of Wi-Fi geaktiveer is en die name van gekoppelde Wi-Fi toestelle, te sien."</string>
+ <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Laat die app toe om inligting oor wi-fi-netwerke, soos of wi-fi geaktiveer is en die name van gekoppelde wi-fi-toestelle, te sien."</string>
<string name="permlab_changeWifiState" msgid="7947824109713181554">"koppel en ontkoppel van Wi-Fi"</string>
<string name="permdesc_changeWifiState" msgid="7170350070554505384">"Laat die program toe om te koppel aan en te ontkoppel van Wi-Fi-toegangspunte en om veranderings aan Wi-Fi-netwerke se toestelopstellings te maak."</string>
<string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"laat Wi-Fi-multisendontvangs toe"</string>
- <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"Laat die program toe om pakkies te ontvang wat met behulp van multisaai-adresse na alle toestelle op \'n Wi-Fi-netwerk gestuur is, nie net jou tablet nie. Dit gebruik meer krag as die nie-multisaaimodus."</string>
- <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Laat die program toe om pakkette te ontvang wat met multi-uitsendingadresse na alle toestelle op \'n Wi-Fi-netwerk gestuur is, nie net jou Android TV-toestel nie. Dit gebruik meer krag as nie-multi-uitsendingmodus."</string>
+ <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"Laat die app toe om pakkies te ontvang wat met behulp van multisaai-adresse na alle toestelle op \'n wi-fi-netwerk gestuur is, nie net jou tablet nie. Dit gebruik meer krag as die nie-multisaaimodus."</string>
+ <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Laat die app toe om pakkette te ontvang wat met multi-uitsendingadresse na alle toestelle op \'n wi-fi-netwerk gestuur is, nie net jou Android TV-toestel nie. Dit gebruik meer krag as nie-multi-uitsendingmodus."</string>
<string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Laat die program toe om pakkies te ontvang wat met behulp van multisaai-adresse na alle toestelle op \'n Wi-Fi-netwerk gestuur is, nie net jou foon nie. Dit gebruik meer krag as die nie-multisaaimodus."</string>
<string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"gaan in by Bluetooth-instellings"</string>
- <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Laat die program toe om die plaaslike Bluetooth-tablet op te stel, en om met afstandbeheer toestelle saam te bind."</string>
- <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Laat die program toe om Bluetooth op jou Android TV-toestel op te stel, en om afgeleë toestelle te ontdek en met hulle saam te bind."</string>
+ <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Laat die app toe om die plaaslike Bluetooth-tablet op te stel, en om met afstandbeheer toestelle saam te bind."</string>
+ <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Laat die app toe om Bluetooth op jou Android TV-toestel op te stel, en om afgeleë toestelle te ontdek en met hulle saam te bind."</string>
<string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"Laat die program toe om die plaaslike Bluetooth-foon op te stel en te ontdek en met afgeleë toestelle saam te bind."</string>
<string name="permlab_accessWimaxState" msgid="7029563339012437434">"koppel aan en ontkoppel van WiMAX"</string>
- <string name="permdesc_accessWimaxState" msgid="5372734776802067708">"Laat die program toe om te bepaal of WiMAX geaktiveer is en of enige WiMAX-netwerke gekoppel is."</string>
+ <string name="permdesc_accessWimaxState" msgid="5372734776802067708">"Laat die app toe om te bepaal of WiMAX geaktiveer is en of enige WiMAX-netwerke gekoppel is."</string>
<string name="permlab_changeWimaxState" msgid="6223305780806267462">"verander WiMAX-status"</string>
- <string name="permdesc_changeWimaxState" product="tablet" msgid="4011097664859480108">"Laat die program toe om die tablet aan WiMAX-netwerke te koppel en daarvan te ontkoppel."</string>
- <string name="permdesc_changeWimaxState" product="tv" msgid="5373274458799425276">"Laat die program toe om jou Android TV-toestel aan WiMAX-netwerke te koppel en daarvan te ontkoppel."</string>
- <string name="permdesc_changeWimaxState" product="default" msgid="1551666203780202101">"Laat die program toe om die foon aan WiMAX-netwerke te koppel en daarvan te ontkoppel."</string>
+ <string name="permdesc_changeWimaxState" product="tablet" msgid="4011097664859480108">"Laat die app toe om die tablet aan WiMAX-netwerke te koppel en daarvan te ontkoppel."</string>
+ <string name="permdesc_changeWimaxState" product="tv" msgid="5373274458799425276">"Laat die app toe om jou Android TV-toestel aan WiMAX-netwerke te koppel en daarvan te ontkoppel."</string>
+ <string name="permdesc_changeWimaxState" product="default" msgid="1551666203780202101">"Laat die app toe om die foon aan WiMAX-netwerke te koppel en daarvan te ontkoppel."</string>
<string name="permlab_bluetooth" msgid="586333280736937209">"bind saam met Bluetooth-toestelle"</string>
- <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Laat die program toe om die opstelling van Bluetooth op die tablet te sien, en om verbindings met saamgebinde toestelle te maak en te aanvaar."</string>
+ <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Laat die app toe om die opstelling van Bluetooth op die tablet te sien, en om verbindings met saamgebinde toestelle te maak en te aanvaar."</string>
<string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Laat die program toe om die opstelling van Bluetooth op jou Android TV-toestel te bekyk, en om verbindings met saamgebinde toestelle te maak en te aanvaar."</string>
- <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Laat die program toe om die opstelling van die Bluetooth op die foon te sien, en om verbindings met saamgebinde toestelle te maak en te aanvaar."</string>
+ <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Laat die app toe om die opstelling van die Bluetooth op die foon te sien, en om verbindings met saamgebinde toestelle te maak en te aanvaar."</string>
<string name="permlab_bluetooth_scan" msgid="5402587142833124594">"ontdek en bind Bluetooth-toestelle in die omtrek saam"</string>
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Laat die program toe om Bluetooth-toestelle in die omtrek te ontdek en saam te bind"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"koppel aan saamgebinde Bluetooth-toestelle"</string>
@@ -627,27 +624,27 @@
<string name="permlab_nfcTransactionEvent" msgid="5868209446710407679">"Veilige Element-transaksiegeval"</string>
<string name="permdesc_nfcTransactionEvent" msgid="1904286701876487397">"Laat die app toe om inligting te ontvang oor transaksies wat op ’n Veilige Element plaasvind."</string>
<string name="permlab_disableKeyguard" msgid="3605253559020928505">"deaktiveer jou skermslot"</string>
- <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Laat die program toe om die sleutelslot en enige verwante wagwoordsekuriteit te deaktiveer. Byvoorbeeld, die foon deaktiveer die sleutelslot wanneer ’n oproep inkom, en atkiveer dit dan weer wanneer die oproep eindig."</string>
+ <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Laat die app toe om die sleutelslot en enige verwante wagwoordsekuriteit te deaktiveer. Byvoorbeeld, die foon deaktiveer die sleutelslot wanneer ’n oproep inkom, en aktiveer dit dan weer wanneer die oproep eindig."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"versoek skermslot-kompleksiteit"</string>
- <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Laat die program toe om die skermslot-kompleksiteitvlak (hoog, medium, laag of geen) te leer, wat die moontlike omvang van die lengte en soort skermslot aandui. Hierdie program kan ook aan gebruikers voorstel dat hulle die skermslot na \'n sekere vlak opdateer, maar gebruikers kan dit uit vrye wil ignoreer en weggaan. Let daarop dat die skermslot nie in skoonteks geberg word nie en die program dus nie die presiese wagwoord ken nie."</string>
+ <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Laat die app toe om die skermslot-kompleksiteitvlak (hoog, medium, laag of geen) te leer, wat die moontlike omvang van die lengte en soort skermslot aandui. Hierdie app kan ook aan gebruikers voorstel dat hulle die skermslot na \'n sekere vlak opdateer, maar gebruikers kan dit uit vrye wil ignoreer en weggaan. Let daarop dat die skermslot nie in skoonteks geberg word nie en die app dus nie die presiese wagwoord ken nie."</string>
<string name="permlab_postNotification" msgid="4875401198597803658">"wys kennisgewings"</string>
<string name="permdesc_postNotification" msgid="5974977162462877075">"Laat die program toe om kennisgewings te wys"</string>
<string name="permlab_turnScreenOn" msgid="219344053664171492">"skakel die skerm aan"</string>
- <string name="permdesc_turnScreenOn" msgid="4394606875897601559">"Laat die program toe om die skerm aan te skakel."</string>
+ <string name="permdesc_turnScreenOn" msgid="4394606875897601559">"Laat die app toe om die skerm aan te skakel."</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"gebruik biometriese hardeware"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Laat die program toe om biometriese hardeware vir stawing te gebruik"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"bestuur vingerafdrukhardeware"</string>
- <string name="permdesc_manageFingerprint" msgid="2025616816437339865">"Laat die program toe om metodes te benut om vingerafdruksjablone vir gebruik by te voeg en uit te vee."</string>
+ <string name="permdesc_manageFingerprint" msgid="2025616816437339865">"Laat die app toe om metodes te benut om vingerafdruktemplate vir gebruik by te voeg en uit te vee."</string>
<string name="permlab_useFingerprint" msgid="1001421069766751922">"gebruik vingerafdrukhardeware"</string>
- <string name="permdesc_useFingerprint" msgid="412463055059323742">"Laat die program toe om vingerafdrukhardeware vir stawing te gebruik"</string>
+ <string name="permdesc_useFingerprint" msgid="412463055059323742">"Laat die app toe om vingerafdrukhardeware vir stawing te gebruik"</string>
<string name="permlab_audioWrite" msgid="8501705294265669405">"wysig jou musiekversameling"</string>
<string name="permdesc_audioWrite" msgid="8057399517013412431">"Laat die program toe om jou musiekversameling te wysig."</string>
<string name="permlab_videoWrite" msgid="5940738769586451318">"wysig jou videoversameling"</string>
- <string name="permdesc_videoWrite" msgid="6124731210613317051">"Laat die program toe om jou videoversameling te wysig."</string>
+ <string name="permdesc_videoWrite" msgid="6124731210613317051">"Laat die app toe om jou videoversameling te wysig."</string>
<string name="permlab_imagesWrite" msgid="1774555086984985578">"wysig jou fotoversameling"</string>
- <string name="permdesc_imagesWrite" msgid="5195054463269193317">"Laat die program toe om jou fotoversameling te wysig."</string>
+ <string name="permdesc_imagesWrite" msgid="5195054463269193317">"Laat die app toe om jou fotoversameling te wysig."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"lees liggings in jou mediaversameling"</string>
- <string name="permdesc_mediaLocation" msgid="597912899423578138">"Laat die program toe om liggings in jou mediaversameling te lees."</string>
+ <string name="permdesc_mediaLocation" msgid="597912899423578138">"Laat die app toe om liggings in jou mediaversameling te lees."</string>
<string name="biometric_app_setting_name" msgid="3339209978734534457">"Gebruik biometrie"</string>
<string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Gebruik biometrie of skermslot"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifieer dat dit jy is"</string>
@@ -765,43 +762,43 @@
<string name="face_error_vendor_unknown" msgid="7387005932083302070">"Iets is fout. Probeer weer."</string>
<string name="face_icon_content_description" msgid="465030547475916280">"Gesig-ikoon"</string>
<string name="permlab_readSyncSettings" msgid="6250532864893156277">"lees sinkroniseer-instellings"</string>
- <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"Laat die program toe om die sinkroniseringinstellings van \'n rekening te lees. Byvoorbeeld, dit kan bepaal of die People-program met \'n rekening gesinkroniseer is."</string>
+ <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"Laat die app toe om die sinkroniseringinstellings van \'n rekening te lees. Byvoorbeeld, dit kan bepaal of die People-app met \'n rekening gesinkroniseer is."</string>
<string name="permlab_writeSyncSettings" msgid="6583154300780427399">"wissel tussen sinkronisasie aan en af"</string>
- <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"Laat \'n program toe om die sinkroniseringinstellings van \'n rekening te verander. Byvoorbeeld, dit kan gebruik word om sinkronisasie van die People-program met \'n ander rekening te aktiveer."</string>
+ <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"Laat \'n app toe om die sinkroniseringinstellings van \'n rekening te verander. Byvoorbeeld, dit kan gebruik word om sinkronisasie van die People-app met \'n ander rekening te aktiveer."</string>
<string name="permlab_readSyncStats" msgid="3747407238320105332">"lees sinkroniseerstatistiek"</string>
- <string name="permdesc_readSyncStats" msgid="3867809926567379434">"Laat \'n program toe om die sinkroniseringstatistieke van \'n rekening te lees, insluitend die geskiedenis van sinkroniseringgebeure en hoeveel data gesinkroniseer is."</string>
+ <string name="permdesc_readSyncStats" msgid="3867809926567379434">"Laat \'n app toe om die sinkroniseringstatistieke van \'n rekening te lees, insluitend die geskiedenis van sinkroniseringgebeure en hoeveel data gesinkroniseer is."</string>
<string name="permlab_sdcardRead" msgid="5791467020950064920">"lees jou gedeelde berging se inhoud"</string>
- <string name="permdesc_sdcardRead" msgid="6872973242228240382">"Laat die program toe om jou gedeelde berging se inhoud te lees."</string>
+ <string name="permdesc_sdcardRead" msgid="6872973242228240382">"Laat die app toe om jou gedeelde berging se inhoud te lees."</string>
<string name="permlab_readMediaAudio" msgid="8723513075731763810">"lees oudiolêers in gedeelde berging"</string>
- <string name="permdesc_readMediaAudio" msgid="5299772574434619399">"Laat die program toe om oudiolêers in jou gedeelde berging te lees."</string>
+ <string name="permdesc_readMediaAudio" msgid="5299772574434619399">"Laat die app toe om oudiolêers in jou gedeelde berging te lees."</string>
<string name="permlab_readMediaVideo" msgid="7768003311260655007">"lees videolêers in gedeelde berging"</string>
- <string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Laat die program toe om videolêers in jou gedeelde berging te lees."</string>
+ <string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Laat die app toe om videolêers in jou gedeelde berging te lees."</string>
<string name="permlab_readMediaImages" msgid="4057590631020986789">"lees prentlêers in gedeelde berging"</string>
- <string name="permdesc_readMediaImages" msgid="5836219373138469259">"Laat die program toe om prentlêers in jou gedeelde berging te lees."</string>
+ <string name="permdesc_readMediaImages" msgid="5836219373138469259">"Laat die app toe om prentlêers in jou gedeelde berging te lees."</string>
<string name="permlab_readVisualUserSelect" msgid="5516204215354667586">"lees prent- en videolêers wat gebruiker in gedeelde berging kies"</string>
<string name="permdesc_readVisualUserSelect" msgid="8027174717714968217">"Laat die app toe om prent- en videolêers te lees wat jy in jou gedeelde berging kies."</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"verander of vee jou gedeelde berging se inhoud uit"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Laat die program toe om jou gedeelde berging se inhoud te skryf."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"maak en/of ontvang SIP-oproepe"</string>
- <string name="permdesc_use_sip" msgid="3590270893253204451">"Laat die program toe om SIP-oproepe te maak en te ontvang."</string>
+ <string name="permdesc_use_sip" msgid="3590270893253204451">"Laat die app toe om SIP-oproepe te maak en te ontvang."</string>
<string name="permlab_register_sim_subscription" msgid="1653054249287576161">"registreer nuwe telekommunikasie-SIM-verbindings"</string>
- <string name="permdesc_register_sim_subscription" msgid="4183858662792232464">"Laat die program toe om nuwe telekommunikasie-SIM-verbindings te registreer."</string>
+ <string name="permdesc_register_sim_subscription" msgid="4183858662792232464">"Laat die app toe om nuwe telekommunikasie-SIM-verbindings te registreer."</string>
<string name="permlab_register_call_provider" msgid="6135073566140050702">"registreer nuwe telekommunikasieverbindings"</string>
- <string name="permdesc_register_call_provider" msgid="4201429251459068613">"Laat die program toe om nuwe telekommunikasieverbindings te registreer."</string>
+ <string name="permdesc_register_call_provider" msgid="4201429251459068613">"Laat die app toe om nuwe telekommunikasieverbindings te registreer."</string>
<string name="permlab_connection_manager" msgid="3179365584691166915">"bestuur telekom-verbindings"</string>
- <string name="permdesc_connection_manager" msgid="1426093604238937733">"Laat die program toe om telekom-verbindings te bestuur."</string>
+ <string name="permdesc_connection_manager" msgid="1426093604238937733">"Laat die app toe om telekom-verbindings te bestuur."</string>
<string name="permlab_bind_incall_service" msgid="5990625112603493016">"beleef interaksie met inoproep-skerm"</string>
- <string name="permdesc_bind_incall_service" msgid="4124917526967765162">"Laat die program beheer wanneer en hoe die gebruiker die inoproep-skerm sien."</string>
+ <string name="permdesc_bind_incall_service" msgid="4124917526967765162">"Laat die app beheer wanneer en hoe die gebruiker die inoproep-skerm sien."</string>
<string name="permlab_bind_connection_service" msgid="5409268245525024736">"werk met telefoniedienste saam"</string>
- <string name="permdesc_bind_connection_service" msgid="6261796725253264518">"Laat die program toe om met telefoniedienste saam te werk om oproepe te maak of ontvang."</string>
+ <string name="permdesc_bind_connection_service" msgid="6261796725253264518">"Laat die app toe om met telefoniedienste saam te werk om oproepe te maak of ontvang."</string>
<string name="permlab_control_incall_experience" msgid="6436863486094352987">"bied \'n inoproep-gebruikerervaring"</string>
- <string name="permdesc_control_incall_experience" msgid="5896723643771737534">"Laat die program toe om \'n inoproep-gebruikerervaring te bied."</string>
+ <string name="permdesc_control_incall_experience" msgid="5896723643771737534">"Laat die app toe om \'n inoproep-gebruikerervaring te bied."</string>
<string name="permlab_readNetworkUsageHistory" msgid="8470402862501573795">"lees netwerkgebruik-geskiedenis"</string>
- <string name="permdesc_readNetworkUsageHistory" msgid="1112962304941637102">"Laat die program toe om historiese netwerkgebruik vir spesifieke netwerke en programme te lees."</string>
+ <string name="permdesc_readNetworkUsageHistory" msgid="1112962304941637102">"Laat die app toe om historiese netwerkgebruik vir spesifieke netwerke en apps te lees."</string>
<string name="permlab_manageNetworkPolicy" msgid="6872549423152175378">"bestuur netwerkbeleid"</string>
- <string name="permdesc_manageNetworkPolicy" msgid="1865663268764673296">"Laat die program toe om netwerkbeleide te bestuur en program-spesifieke reëls te definieer."</string>
+ <string name="permdesc_manageNetworkPolicy" msgid="1865663268764673296">"Laat die app toe om netwerkbeleide te bestuur en app-spesifieke reëls te definieer."</string>
<string name="permlab_modifyNetworkAccounting" msgid="7448790834938749041">"verander verrekening van netwerkgebruik"</string>
- <string name="permdesc_modifyNetworkAccounting" msgid="5076042642247205390">"Laat die program toe om te verander hoe netwerkgebruik teenoor programme gemeet word. Nie vir gebruik deur normale programme nie."</string>
+ <string name="permdesc_modifyNetworkAccounting" msgid="5076042642247205390">"Laat die app toe om te verander hoe netwerkgebruik teenoor apps gemeet word. Nie vir gebruik deur normale apps nie."</string>
<string name="permlab_accessNotifications" msgid="7130360248191984741">"kry toegang tot kennisgewings"</string>
<string name="permdesc_accessNotifications" msgid="761730149268789668">"Laat die program toe om kennisgewings op te haal, te bestudeer en te verwyder, insluitende die kennisgewings wat deur ander programme geplaas is."</string>
<string name="permlab_bindNotificationListenerService" msgid="5848096702733262458">"bind aan \'n kennisgewingluisteraardiens"</string>
@@ -813,15 +810,15 @@
<string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"roep die opstellingprogram op wat deur die diensverskaffer voorsien is"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"Laat die houer toe om die opstellingsprogram wat deur die diensverskaffer voorsien word, op te roep. Behoort nooit vir gewone programme nodig te wees nie."</string>
<string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"luister vir waarnemings oor netwerktoestande"</string>
- <string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"Laat \'n program luister vir waarnemings oor netwerktoestande. Behoort nooit nodig te wees vir normale programme nie."</string>
+ <string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"Laat \'n app luister vir waarnemings oor netwerktoestande. Dit behoort nooit vir normale apps nodig te wees nie."</string>
<string name="permlab_setInputCalibration" msgid="932069700285223434">"verander invoertoestelkalibrasie"</string>
- <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"Laat die program toe om die kalibrasieparameters van die raakskerm te wysig. Dit behoort nooit vir normale programme nodig te wees nie."</string>
+ <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"Laat die app toe om die kalibrasieparameters van die raakskerm te wysig. Dit behoort nooit vir normale apps nodig te wees nie."</string>
<string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"gaan in by DRM-sertifikate"</string>
- <string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"Laat \'n program toe om DRM-sertifikate op te stel en te gebruik. Behoort nooit vir normale programme nodig te wees nie."</string>
+ <string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"Laat \'n app toe om DRM-sertifikate op te stel en te gebruik. Dit behoort nooit vir normale apps nodig te wees nie."</string>
<string name="permlab_handoverStatus" msgid="7620438488137057281">"ontvang Android Straal-oordragstatus"</string>
- <string name="permdesc_handoverStatus" msgid="3842269451732571070">"Laat hierdie program toe om inligting oor huidige Android Straal-oordragte te ontvang."</string>
+ <string name="permdesc_handoverStatus" msgid="3842269451732571070">"Laat hierdie app toe om inligting oor huidige Android Straal-oordragte te ontvang."</string>
<string name="permlab_removeDrmCertificates" msgid="710576248717404416">"verwyder DRM-sertifikate"</string>
- <string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"Laat \'n program toe om DRM-sertifikate te verwyder. Behoort nooit vir gewone programme nodig te wees nie."</string>
+ <string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"Laat \'n app toe om DRM-sertifikate te verwyder. Dit behoort nooit vir gewone apps nodig te wees nie."</string>
<string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"bind aan \'n diensverskaffer-boodskapdiens"</string>
<string name="permdesc_bindCarrierMessagingService" msgid="6316457028173478345">"Dit laat die houer toe om aan die top-koppelvlak van \'n diensverskaffer-boodskapdiens te bind. Behoort nooit vir gewone programme nodig te wees nie."</string>
<string name="permlab_bindCarrierServices" msgid="2395596978626237474">"verbind aan diensverskafferdienste"</string>
@@ -829,7 +826,7 @@
<string name="permlab_access_notification_policy" msgid="5524112842876975537">"verkry toegang tot Moenie Steur Nie"</string>
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Laat die program toe om Moenie Steur Nie-opstelling te lees en skryf."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"begin kyk van toestemminggebruik"</string>
- <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Laat die houer toe om die toestemminggebruik vir \'n program te begin. Behoort nooit vir normale programme nodig te wees nie."</string>
+ <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Laat die houer toe om die toestemminggebruik vir \'n app te begin. Dit behoort nooit vir normale apps nodig te wees nie."</string>
<string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"begin Bekyk Toestemmingbesluite"</string>
<string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Laat die houer toe om skerm te begin om toestemmingbesluite na te gaan. Behoort nooit vir normale programme nodig te wees nie."</string>
<string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"begin Bekyk Programkenmerke"</string>
@@ -989,7 +986,7 @@
<string name="sipAddressTypeHome" msgid="5918441930656878367">"Tuis"</string>
<string name="sipAddressTypeWork" msgid="7873967986701216770">"Werk"</string>
<string name="sipAddressTypeOther" msgid="6317012577345187275">"Ander"</string>
- <string name="quick_contacts_not_available" msgid="1262709196045052223">"Geen program gekry om hierdie kontak te bekyk nie."</string>
+ <string name="quick_contacts_not_available" msgid="1262709196045052223">"Geen app gekry om hierdie kontak te bekyk nie."</string>
<string name="keyguard_password_enter_pin_code" msgid="6401406801060956153">"Voer PIN-kode in"</string>
<string name="keyguard_password_enter_puk_code" msgid="3112256684547584093">"Voer PUK en nuwe PIN-kode in"</string>
<string name="keyguard_password_enter_puk_prompt" msgid="2825313071899938305">"PUK-kode"</string>
@@ -1107,9 +1104,9 @@
<string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nIs jy seker dat jy weg van hierdie bladsy af wil navigeer?"</string>
<string name="autofill_window_title" msgid="4379134104008111961">"Outovul met <xliff:g id="SERVICENAME">%1$s</xliff:g>"</string>
<string name="permlab_setAlarm" msgid="1158001610254173567">"stel \'n wekker"</string>
- <string name="permdesc_setAlarm" msgid="2185033720060109640">"Laat die program toe om \'n alarm in \'n geïnstalleerde wekkerprogram te stel. Sommige wekkerprogramme werk dalk nie met hierdie funksie nie."</string>
+ <string name="permdesc_setAlarm" msgid="2185033720060109640">"Laat die app toe om \'n alarm in \'n geïnstalleerde wekkerapp te stel. Sommige wekkerapps werk dalk nie met hierdie funksie nie."</string>
<string name="permlab_addVoicemail" msgid="4770245808840814471">"voeg stemboodskap by"</string>
- <string name="permdesc_addVoicemail" msgid="5470312139820074324">"Laat die program toe om boodskappe by te voeg by jou stempos-inkassie."</string>
+ <string name="permdesc_addVoicemail" msgid="5470312139820074324">"Laat die app toe om boodskappe by te voeg by jou stempos-inkassie."</string>
<string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> het van jou knipbord af geplak"</string>
<string name="more_item_label" msgid="7419249600215749115">"Meer"</string>
<string name="prepend_shortcut_label" msgid="1743716737502867951">"Kieslys+"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"oor <xliff:g id="COUNT">%d</xliff:g> u."</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"oor <xliff:g id="COUNT">%d</xliff:g> d."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"oor <xliff:g id="COUNT">%d</xliff:g> j."</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuut gelede}other{# minute gelede}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# uur gelede}other{# uur gelede}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# dag gelede}other{# dae gelede}}"</string>
@@ -1208,7 +1237,7 @@
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Sommige stelselfunksies werk moontlik nie"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nie genoeg berging vir die stelsel nie. Maak seker jy het 250 MB spasie beskikbaar en herbegin."</string>
<string name="app_running_notification_title" msgid="8985999749231486569">"<xliff:g id="APP_NAME">%1$s</xliff:g> loop tans"</string>
- <string name="app_running_notification_text" msgid="5120815883400228566">"Tik vir meer inligting of om die program te stop."</string>
+ <string name="app_running_notification_text" msgid="5120815883400228566">"Tik vir meer inligting of om die app te stop."</string>
<string name="ok" msgid="2646370155170753815">"OK"</string>
<string name="cancel" msgid="6908697720451760115">"Kanselleer"</string>
<string name="yes" msgid="9069828999585032361">"OK"</string>
@@ -1250,16 +1279,16 @@
<string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"Vang prent vas met %1$s"</string>
<string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"Vang prent vas"</string>
<string name="alwaysUse" msgid="3153558199076112903">"Gebruik hierdie aksie by verstek."</string>
- <string name="use_a_different_app" msgid="4987790276170972776">"Gebruik \'n ander program"</string>
+ <string name="use_a_different_app" msgid="4987790276170972776">"Gebruik \'n ander app"</string>
<string name="clearDefaultHintMsg" msgid="1325866337702524936">"Vee die verstek instelling uit in Stelselinstellings > Programme > Afgelaai."</string>
<string name="chooseActivity" msgid="8563390197659779956">"Kies \'n handeling"</string>
- <string name="chooseUsbActivity" msgid="2096269989990986612">"Kies \'n program vir die USB-toestel"</string>
+ <string name="chooseUsbActivity" msgid="2096269989990986612">"Kies \'n app vir die USB-toestel"</string>
<string name="noApplications" msgid="1186909265235544019">"Geen programme kan hierdie handeling uitvoer nie."</string>
<string name="aerr_application" msgid="4090916809370389109">"<xliff:g id="APPLICATION">%1$s</xliff:g> het gestop"</string>
<string name="aerr_process" msgid="4268018696970966407">"<xliff:g id="PROCESS">%1$s</xliff:g> het gestop"</string>
<string name="aerr_application_repeated" msgid="7804378743218496566">"<xliff:g id="APPLICATION">%1$s</xliff:g> stop aanhoudend"</string>
<string name="aerr_process_repeated" msgid="1153152413537954974">"<xliff:g id="PROCESS">%1$s</xliff:g> stop aanhoudend"</string>
- <string name="aerr_restart" msgid="2789618625210505419">"Maak program weer oop"</string>
+ <string name="aerr_restart" msgid="2789618625210505419">"Maak app weer oop"</string>
<string name="aerr_report" msgid="3095644466849299308">"Stuur terugvoer"</string>
<string name="aerr_close" msgid="3398336821267021852">"Maak toe"</string>
<string name="aerr_mute" msgid="2304972923480211376">"Demp totdat toestel herbegin"</string>
@@ -1274,7 +1303,7 @@
<string name="report" msgid="2149194372340349521">"Verslag"</string>
<string name="wait" msgid="7765985809494033348">"Wag"</string>
<string name="webpage_unresponsive" msgid="7850879412195273433">"Die bladsy reageer nie meer nie.\n\nWil jy dit toemaak?"</string>
- <string name="launch_warning_title" msgid="6725456009564953595">"Program herlei"</string>
+ <string name="launch_warning_title" msgid="6725456009564953595">"App is herlei"</string>
<string name="launch_warning_replace" msgid="3073392976283203402">"<xliff:g id="APP_NAME">%1$s</xliff:g> loop nou."</string>
<string name="launch_warning_original" msgid="3332206576800169626">"<xliff:g id="APP_NAME">%1$s</xliff:g> is oorspronklik laat loop."</string>
<string name="screen_compat_mode_scale" msgid="8627359598437527726">"Skaal"</string>
@@ -1285,7 +1314,7 @@
<string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"<xliff:g id="APP_NAME">%1$s</xliff:g> is gebou vir \'n onversoenbare weergawe van die Android-bedryfstelsel en kan dalk op \'n onverwagte manier reageer. \'n Opgedateerde weergawe van die program is dalk beskikbaar."</string>
<string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"Wys altyd"</string>
<string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"Kyk vir opdatering"</string>
- <string name="smv_application" msgid="3775183542777792638">"Die program <xliff:g id="APPLICATION">%1$s</xliff:g> (proses <xliff:g id="PROCESS">%2$s</xliff:g>) het sy selfopgelegde StrictMode-beleid oortree."</string>
+ <string name="smv_application" msgid="3775183542777792638">"Die app <xliff:g id="APPLICATION">%1$s</xliff:g> (proses <xliff:g id="PROCESS">%2$s</xliff:g>) het sy selfopgelegde StrictMode-beleid oortree."</string>
<string name="smv_process" msgid="1398801497130695446">"Die proses <xliff:g id="PROCESS">%1$s</xliff:g> het die selfopgelegde StrictMode-beleid geskend."</string>
<string name="android_upgrading_title" product="default" msgid="7279077384220829683">"Foon dateer tans op …"</string>
<string name="android_upgrading_title" product="tablet" msgid="4268417249079938805">"Tablet dateer tans oop …"</string>
@@ -1317,7 +1346,7 @@
<string name="dump_heap_ready_notification" msgid="2302452262927390268">"<xliff:g id="PROC">%1$s</xliff:g>-hoopstorting is gereed"</string>
<string name="dump_heap_notification_detail" msgid="8431586843001054050">"Hoopstorting is ingesamel. Tik om te deel."</string>
<string name="dump_heap_title" msgid="4367128917229233901">"Deel hoopstorting?"</string>
- <string name="dump_heap_text" msgid="1692649033835719336">"Die <xliff:g id="PROC">%1$s</xliff:g>-proses het sy geheuelimiet van <xliff:g id="SIZE">%2$s</xliff:g> oorskry. \'n Hoopstorting is beskikbaar wat jy met sy ontwikkelaar kan deel. Pas op: Hierdie hoopstorting bevat dalk van jou persoonlike inligting waartoe die program toegang het."</string>
+ <string name="dump_heap_text" msgid="1692649033835719336">"Die <xliff:g id="PROC">%1$s</xliff:g>-proses het sy geheuelimiet van <xliff:g id="SIZE">%2$s</xliff:g> oorskry. \'n Hoopstorting is beskikbaar wat jy met sy ontwikkelaar kan deel. Pas op: Hierdie hoopstorting bevat dalk van jou persoonlike inligting waartoe die app toegang het."</string>
<string name="dump_heap_system_text" msgid="6805155514925350849">"Die <xliff:g id="PROC">%1$s</xliff:g>-proses het sy geheuelimiet van <xliff:g id="SIZE">%2$s</xliff:g> oorskry. \'n Hoopstorting is beskikbaar wat jy kan deel. Wees versigtig: Hierdie hoopstorting kan enige sensitiewe persoonlike inligting bevat waarby die proses kan ingaan, wat goed kan insluit wat jy getik het."</string>
<string name="dump_heap_ready_text" msgid="5849618132123045516">"\'n Hoopstorting van <xliff:g id="PROC">%1$s</xliff:g> se proses is beskikbaar vir jou om te deel. Wees versigtig: Hierdie hoopstorting kan sensitiewe persoonlike inligting bevat waarby die proses kan ingaan, wat goed kan insluit wat jy getik het."</string>
<string name="sendText" msgid="493003724401350724">"Kies \'n handeling vir teks"</string>
@@ -1368,7 +1397,7 @@
<string name="decline" msgid="6490507610282145874">"Weier"</string>
<string name="select_character" msgid="3352797107930786979">"Voeg karakter in"</string>
<string name="sms_control_title" msgid="4748684259903148341">"Stuur SMS-boodskappe"</string>
- <string name="sms_control_message" msgid="6574313876316388239">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> stuur \'n groot aantal SMS-boodskappe. Wil jy hierdie program toelaat om voort te gaan om boodskappe te stuur?"</string>
+ <string name="sms_control_message" msgid="6574313876316388239">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> stuur \'n groot aantal SMS-boodskappe. Wil jy hierdie app toelaat om voort te gaan om boodskappe te stuur?"</string>
<string name="sms_control_yes" msgid="4858845109269524622">"Laat toe"</string>
<string name="sms_control_no" msgid="4845717880040355570">"Weier"</string>
<string name="sms_short_code_confirm_message" msgid="1385416688897538724">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> wil \'n boodskap na <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b> stuur."</string>
@@ -1388,8 +1417,8 @@
<string name="sim_restart_button" msgid="8481803851341190038">"Herbegin"</string>
<string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Aktiveer mobiele diens"</string>
<string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Laai die diensverskafferprogram af om jou nuwe SIM te aktiveer"</string>
- <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Laai die <xliff:g id="APP_NAME">%1$s</xliff:g>-program af om jou nuwe SIM te aktiveer"</string>
- <string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Laai program af"</string>
+ <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Laai die <xliff:g id="APP_NAME">%1$s</xliff:g>-app af om jou nuwe SIM te aktiveer"</string>
+ <string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Laai app af"</string>
<string name="carrier_app_notification_title" msgid="5815477368072060250">"Nuwe SIM is ingesit"</string>
<string name="carrier_app_notification_text" msgid="6567057546341958637">"Tik om dit op te stel"</string>
<string name="time_picker_dialog_title" msgid="9053376764985220821">"Stel tyd"</string>
@@ -1502,15 +1531,15 @@
<string name="ext_media_status_missing" msgid="6520746443048867314">"Nie ingevoeg nie"</string>
<string name="activity_list_empty" msgid="4219430010716034252">"Geen passende aktiwiteite gevind nie."</string>
<string name="permlab_route_media_output" msgid="8048124531439513118">"roeteer media-uitvoer"</string>
- <string name="permdesc_route_media_output" msgid="1759683269387729675">"Laat \'n program toe om media-uitvoere na ander eksterne toestelle te roeteer."</string>
+ <string name="permdesc_route_media_output" msgid="1759683269387729675">"Laat \'n app toe om media-uitvoere na ander eksterne toestelle te roeteer."</string>
<string name="permlab_readInstallSessions" msgid="7279049337895583621">"lees installeersessies"</string>
- <string name="permdesc_readInstallSessions" msgid="4012608316610763473">"Laat \'n program toe om installasiesessies te lees. Dit laat dit toe om besonderhede van aktiewe pakketinstallasies te sien."</string>
+ <string name="permdesc_readInstallSessions" msgid="4012608316610763473">"Laat \'n app toe om installasiesessies te lees. Dit laat dit toe om besonderhede van aktiewe pakketinstallasies te sien."</string>
<string name="permlab_requestInstallPackages" msgid="7600020863445351154">"versoek installeerpakkette"</string>
- <string name="permdesc_requestInstallPackages" msgid="3969369278325313067">"Laat \'n program toe om te versoek dat pakkette geïnstalleer word."</string>
+ <string name="permdesc_requestInstallPackages" msgid="3969369278325313067">"Laat \'n app toe om te versoek dat pakkette geïnstalleer word."</string>
<string name="permlab_requestDeletePackages" msgid="2541172829260106795">"versoek uitvee van pakkette"</string>
- <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Laat \'n program toe om te versoek dat pakkette uitgevee word."</string>
+ <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Laat \'n app toe om te versoek dat pakkette uitgevee word."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"vra om batteryoptimerings te ignoreer"</string>
- <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Laat \'n program toe om toestemming te vra om batteryoptimerings vir daardie program ignoreer."</string>
+ <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Laat \'n app toe om toestemming te vra om batteryoptimerings vir daardie app te ignoreer."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"navraag oor alle pakkette"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Laat \'n program toe om alle geïnstalleerde pakette te sien."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Klop twee keer vir zoembeheer"</string>
@@ -1532,8 +1561,8 @@
<string name="permission_request_notification_title" msgid="1810025922441048273">"Toestemming versoek"</string>
<string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"Toestemming versoek\nvir rekening <xliff:g id="ACCOUNT">%s</xliff:g>."</string>
<string name="permission_request_notification_for_app_with_subtitle" msgid="1298704005732851350">"Toestemming versoek deur <xliff:g id="APP">%1$s</xliff:g>\nvir rekening <xliff:g id="ACCOUNT">%2$s</xliff:g>"</string>
- <string name="forward_intent_to_owner" msgid="4620359037192871015">"Jy gebruik hierdie program buite jou werkprofiel"</string>
- <string name="forward_intent_to_work" msgid="3620262405636021151">"Jy gebruik tans hierdie program in jou werkprofiel"</string>
+ <string name="forward_intent_to_owner" msgid="4620359037192871015">"Jy gebruik hierdie app buite jou werkprofiel"</string>
+ <string name="forward_intent_to_work" msgid="3620262405636021151">"Jy gebruik tans hierdie app in jou werkprofiel"</string>
<string name="input_method_binding_label" msgid="1166731601721983656">"Invoermetode"</string>
<string name="sync_binding_label" msgid="469249309424662147">"Sinkroniseer"</string>
<string name="accessibility_binding_label" msgid="1974602776545801715">"Toeganklikheid"</string>
@@ -1609,7 +1638,7 @@
<string name="keyboardview_keycode_mode_change" msgid="2743735349997999020">"Modus verander"</string>
<string name="keyboardview_keycode_shift" msgid="3026509237043975573">"Shift"</string>
<string name="keyboardview_keycode_enter" msgid="168054869339091055">"Invoersleutel"</string>
- <string name="activitychooserview_choose_application" msgid="3500574466367891463">"Kies \'n program"</string>
+ <string name="activitychooserview_choose_application" msgid="3500574466367891463">"Kies \'n app"</string>
<string name="activitychooserview_choose_application_error" msgid="6937782107559241734">"Kon <xliff:g id="APPLICATION_NAME">%s</xliff:g> nie begin nie"</string>
<string name="shareactionprovider_share_with" msgid="2753089758467748982">"Deel met"</string>
<string name="shareactionprovider_share_with_application" msgid="4902832247173666973">"Deel met <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
@@ -1782,7 +1811,7 @@
<string name="guest_name" msgid="8502103277839834324">"Gas"</string>
<string name="error_message_title" msgid="4082495589294631966">"Fout"</string>
<string name="error_message_change_not_allowed" msgid="843159705042381454">"Jou administrateur laat nie hierdie verandering toe nie"</string>
- <string name="app_not_found" msgid="3429506115332341800">"Geen program gevind om hierdie handeling te hanteer nie"</string>
+ <string name="app_not_found" msgid="3429506115332341800">"Geen app gevind om hierdie handeling te hanteer nie"</string>
<string name="revoke" msgid="5526857743819590458">"Herroep"</string>
<string name="mediasize_iso_a0" msgid="7039061159929977973">"ISO A0"</string>
<string name="mediasize_iso_a1" msgid="4063589931031977223">"ISO A1"</string>
@@ -2039,9 +2068,9 @@
<string name="app_streaming_blocked_message_for_permission_request" product="tv" msgid="4706276040125072077">"Hierdie app versoek tans bykomende toestemmings, maar toestemmings kan nie in ’n stromingsessie verleen word nie. Verleen eers die toestemming op jou Android TV-toestel."</string>
<string name="app_streaming_blocked_message_for_permission_request" product="tablet" msgid="1824604581465771629">"Hierdie app versoek tans bykomende toestemmings, maar toestemmings kan nie in ’n stromingsessie verleen word nie. Verleen eers die toestemming op jou tablet."</string>
<string name="app_streaming_blocked_message_for_permission_request" product="default" msgid="7755223160363292105">"Hierdie app versoek tans bykomende toestemmings, maar toestemmings kan nie in ’n stromingsessie verleen word nie. Verleen eers die toestemming op jou foon."</string>
- <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Hierdie program versoek tans bykomende sekuriteit. Probeer eerder op jou Android TV-toestel."</string>
- <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"Hierdie program versoek tans bykomende sekuriteit. Probeer eerder op jou tablet."</string>
- <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Hierdie program versoek tans bykomende sekuriteit. Probeer eerder op jou foon."</string>
+ <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Hierdie app versoek tans bykomende sekuriteit. Probeer eerder op jou Android TV-toestel."</string>
+ <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"Hierdie app versoek tans bykomende sekuriteit. Probeer eerder op jou tablet."</string>
+ <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Hierdie app versoek tans bykomende sekuriteit. Probeer eerder op jou foon."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Jy kan nie op jou <xliff:g id="DEVICE">%1$s</xliff:g> toegang hiertoe kry nie. Probeer eerder op jou Android TV-toestel."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Jy kan nie op jou <xliff:g id="DEVICE">%1$s</xliff:g> toegang hiertoe kry nie. Probeer eerder op jou tablet."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Jy kan nie op jou <xliff:g id="DEVICE">%1$s</xliff:g> toegang hiertoe kry nie. Probeer eerder op jou foon."</string>
@@ -2049,7 +2078,7 @@
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Kyk vir opdatering"</string>
<string name="deprecated_abi_message" msgid="6820548011196218091">"Hierdie app is nie met die jongste weergawe van Android versoenbaar nie. Kyk of daar ’n opdatering is, of kontak die app se ontwikkelaar."</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Jy het nuwe boodskappe"</string>
- <string name="new_sms_notification_content" msgid="3197949934153460639">"Maak SMS-program oop om te bekyk"</string>
+ <string name="new_sms_notification_content" msgid="3197949934153460639">"Maak SMS-app oop om te bekyk"</string>
<string name="profile_encrypted_title" msgid="9001208667521266472">"Sommige funksies kan beperk wees"</string>
<string name="profile_encrypted_detail" msgid="5279730442756849055">"Werkprofiel is gesluit"</string>
<string name="profile_encrypted_message" msgid="1128512616293157802">"Tik om werkprofiel te ontsluit"</string>
@@ -2128,7 +2157,7 @@
<string name="popup_window_default_title" msgid="6907717596694826919">"Opspringvenster"</string>
<string name="slice_more_content" msgid="3377367737876888459">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="9206301954024286063">"Programweergawe is afgegradeer, of is nie met hierdie kortpad versoenbaar nie"</string>
- <string name="shortcut_restore_not_supported" msgid="4763198938588468400">"Kon nie die kortpad teruglaai nie omdat die program nie rugsteun en teruglaai steun nie"</string>
+ <string name="shortcut_restore_not_supported" msgid="4763198938588468400">"Kon nie die kortpad teruglaai nie omdat die app nie rugsteun en teruglaai steun nie"</string>
<string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"Kon nie teruglaai nie omdat programondertekening nie ooreenstem nie"</string>
<string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"Kon nie kortpad teruglaai nie"</string>
<string name="shortcut_disabled_reason_unknown" msgid="753074793553599166">"Kortpad is gedeaktiveer"</string>
@@ -2171,7 +2200,7 @@
<string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"Tablet se battery het genoeg krag. Kenmerke is nie meer beperk nie."</string>
<string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"Toestel se battery het genoeg krag. Kenmerke is nie meer beperk nie."</string>
<string name="mime_type_folder" msgid="2203536499348787650">"Vouer"</string>
- <string name="mime_type_apk" msgid="3168784749499623902">"Android-program"</string>
+ <string name="mime_type_apk" msgid="3168784749499623902">"Android-app"</string>
<string name="mime_type_generic" msgid="4606589110116560228">"Lêer"</string>
<string name="mime_type_generic_ext" msgid="9220220924380909486">"<xliff:g id="EXTENSION">%1$s</xliff:g>-lêer"</string>
<string name="mime_type_audio" msgid="4933450584432509875">"Oudio"</string>
@@ -2193,7 +2222,7 @@
<string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} + # lêer}other{{file_name} + # lêers}}"</string>
<string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Geen mense om mee te deel is aanbeveel nie"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Programmelys"</string>
- <string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Opneemtoestemming is nie aan hierdie program verleen nie, maar dit kan oudio deur hierdie USB-toestel opneem."</string>
+ <string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Opneemtoestemming is nie aan hierdie app verleen nie, maar dit kan oudio deur hierdie USB-toestel opneem."</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"Tuis"</string>
<string name="accessibility_system_action_back_label" msgid="4205361367345537608">"Terug"</string>
<string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Onlangse programme"</string>
@@ -2374,8 +2403,8 @@
<string name="ui_translation_accessibility_translated_text" msgid="3197547218178944544">"<xliff:g id="MESSAGE">%1$s</xliff:g> is vertaal."</string>
<string name="ui_translation_accessibility_translation_finished" msgid="3057830947610088465">"Boodskap is vertaal uit <xliff:g id="FROM_LANGUAGE">%1$s</xliff:g> in <xliff:g id="TO_LANGUAGE">%2$s</xliff:g>."</string>
<string name="notification_channel_abusive_bg_apps" msgid="6092140213264920355">"Agtergrondaktiwiteit"</string>
- <string name="notification_title_abusive_bg_apps" msgid="994230770856147656">"’n Program maak tans die battery pap"</string>
- <string name="notification_title_long_running_fgs" msgid="8170284286477131587">"’n Program is nog aktief"</string>
+ <string name="notification_title_abusive_bg_apps" msgid="994230770856147656">"’n App maak tans die battery pap"</string>
+ <string name="notification_title_long_running_fgs" msgid="8170284286477131587">"’n App is nog aktief"</string>
<string name="notification_content_abusive_bg_apps" msgid="5296898075922695259">"<xliff:g id="APP">%1$s</xliff:g> loop tans op die agtergrond. Tik om batterygebruik te bestuur."</string>
<string name="notification_content_long_running_fgs" msgid="8258193410039977101">"<xliff:g id="APP">%1$s</xliff:g> kan batterylewe beïnvloed. Tik om aktiewe programme na te gaan."</string>
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Gaan aktiewe programme na"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index dcb7186..ad5bbcb 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"የደዋይ ID ወደ ተከልክሏል ነባሪዎች።ቀጥሎ ጥሪ፡ አልተከለከለም"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"የደዋይ ID ወደ አልተከለከለም ነባሪዎች።ቀጥሎ ጥሪ፡ ተከልክሏል"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"የደዋይ ID ነባሪዎች ወደአልተከለከለም። ቀጥሎ ጥሪ፡አልተከለከለም"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"ይህ መተግበሪያ ለ16 ኪባ ተኳዃኝ አይደለም። የኤፒኬ አሰላለፍ ፍተሻ አልተሳካም። ይህ መተግበሪያ የገፅ መጠን ተኳዃኝ ሁነታን በመጠቀም ይሄዳል። ለምርጥ ተኳዃኝነት እባክዎ መተግበሪያውን በ16 ኪባ ድጋፍ እንደገና ያጠናቅሩ። ለተጨማሪ መረጃ <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> የሚለውን ይመልከቱ"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"ይህ መተግበሪያ ለ16 ኪባ ተኳዃኝ አይደለም። የELF አሰላለፍ ፍተሻ አልተሳካም። ይህ መተግበሪያ የገፅ መጠን ተኳዃኝ ሁነታን በመጠቀም ይሄዳል። ለምርጥ ተኳዃኝነት እባክዎ መተግበሪያውን በ16 ኪባ ድጋፍ እንደገና ያጠናቅሩ። ለተጨማሪ መረጃ <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> የሚለውን ይመልከቱ"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"ይህ መተግበሪያ ለ16 ኪባ ተኳዃኝ አይደለም። ኤፒኬ እና ELF አሰላለፍ ፍተሻዎች አልተሳኩም። ይህ መተግበሪያ የገፅ መጠን ተኳዃኝ ሁነታን በመጠቀም ይሄዳል። ለምርጥ ተኳዃኝነት እባክዎ መተግበሪያውን በ16 ኪባ ድጋፍ እንደገና ያጠናቅሩ። ለተጨማሪ መረጃ <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> የሚለውን ይመልከቱ"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"አገልግሎት አልቀረበም።"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"የደዋይ መታወቂያ ቅንብሮች መለወጥ አትችልም፡፡"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"ውሂብ ወደ <xliff:g id="CARRIERDISPLAY">%s</xliff:g> ተቀይሯል"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"በ<xliff:g id="COUNT">%d</xliff:g> ሰ ውስጥ"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"በ<xliff:g id="COUNT">%d</xliff:g> ቀ ውስጥ"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"በ<xliff:g id="COUNT">%d</xliff:g> ዓ ውስጥ"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{ከ# ደቂቃ በፊት}one{# ደቂቃ በፊት}other{# ደቂቃዎች በፊት}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{ከ# ሰዓት በፊት}one{ከ# ሰዓት በፊት}other{ከ# ሰዓታት በፊት}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{ከ# ቀን በፊት}one{ከ# ቀን በፊት}other{ከ# ቀናት በፊት}}"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 9c590ad..8cb64c5 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1162,6 +1162,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"خلال <xliff:g id="COUNT">%d</xliff:g> ساعة"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"خلال <xliff:g id="COUNT">%d</xliff:g> يوم"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"خلال <xliff:g id="COUNT">%d</xliff:g> سنة"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{قبل دقيقة واحدة}zero{قبل # دقيقة}two{قبل دقيقتين}few{قبل # دقائق}many{قبل # دقيقة}other{قبل # دقيقة}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{قبل ساعة واحدة}zero{قبل # ساعة}two{قبل ساعتين}few{قبل # ساعات}many{قبل # ساعة}other{قبل # ساعة}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{قبل يوم واحد}zero{قبل # يوم}two{قبل يومين}few{قبل # أيام}many{قبل # يومًا}other{قبل # يوم}}"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 319cf4bd..4214ebe 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"কলাৰ আইডি সীমিত কৰিবলৈ পূর্বনির্ধাৰণ কৰা হৈছে। পৰৱৰ্তী কল: সীমিত কৰা হৈছে"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"কলাৰ আইডি সীমিত নকৰিবলৈ পূর্বনির্ধাৰণ কৰা হৈছে। পৰৱৰ্তী কল: সীমিত কৰা হোৱা নাই"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"কলাৰ আইডি সীমিত নকৰিবলৈ পূর্বনির্ধাৰণ কৰা হৈছে। পৰৱৰ্তী কল: সীমিত কৰা হোৱা নাই"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"এই এপ্টো ১৬ কেবিৰ সমিল নহয়। APKৰ শাৰীবিন্যাস পৰীক্ষা বিফল হৈছে। এই এপ্টো পৃষ্ঠাৰ আকাৰৰ সমিল ম’ড ব্যৱহাৰ কৰি চলোৱা হ’ব। উত্তম সমিলতাৰ বাবে, অনুগ্ৰহ কৰি এপ্লিকেশ্বনটো ১৬ কেবিৰ সমৰ্থনৰ সৈতে পুনৰায় কম্পাইল কৰক। অধিক তথ্যৰ বাবে, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>লৈ যাওক"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"এই এপ্টো ১৬ কেবিৰ সমিল নহয়। ELFৰ শাৰীবিন্যাস পৰীক্ষা বিফল হৈছে। এই এপ্টো পৃষ্ঠাৰ আকাৰৰ সমিল ম’ড ব্যৱহাৰ কৰি চলোৱা হ’ব। উত্তম সমিলতাৰ বাবে, অনুগ্ৰহ কৰি এপ্লিকেশ্বনটো ১৬ কেবিৰ সমৰ্থনৰ সৈতে পুনৰায় কম্পাইল কৰক। অধিক তথ্যৰ বাবে, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>লৈ যাওক"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"এই এপ্টো ১৬ কেবিৰ সমিল নহয়। APK আৰু ELFৰ শাৰীবিন্যাস পৰীক্ষা বিফল হৈছে। এই এপ্টো পৃষ্ঠাৰ আকাৰৰ সমিল ম’ড ব্যৱহাৰ কৰি চলোৱা হ’ব। উত্তম সমিলতাৰ বাবে, অনুগ্ৰহ কৰি এপ্লিকেশ্বনটো ১৬ কেবিৰ সমৰ্থনৰ সৈতে পুনৰায় কম্পাইল কৰক। অধিক তথ্যৰ বাবে, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>লৈ যাওক"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"সুবিধা যোগান ধৰা হোৱা নাই।"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"আপুনি কলাৰ আইডি ছেটিং সলনি কৰিব নোৱাৰে।"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"ডেটা <xliff:g id="CARRIERDISPLAY">%s</xliff:g>লৈ সলনি কৰা হৈছে"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> ঘণ্টাত"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>দিনত"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> বছৰত"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# মিনিট পূৰ্বে}one{# মিনিট পূৰ্বে}other{# মিনিট পূৰ্বে}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ঘণ্টা পূৰ্বে}one{# ঘণ্টা পূৰ্বে}other{# ঘণ্টা পূৰ্বে}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# দিন পূর্বে}one{# দিন পূৰ্বে}other{# দিন পূৰ্বে}}"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 1f8fe2c..d4389dc 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Zəng edənin kimliyi defolt olaraq qadağan deyil. Növbəti zəng: Qadağan deyil"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Zəng edənin kimliyi defolt olaraq qadağan deyil. Növbəti zəng: Qadağandır"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Zəng edənin kimliyi defolt olaraq qadağan deyil. Növbəti zəng: Qadağan deyil"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Bu tətbiq 16 KB ilə uyğunlaşmır. APK düzləndirmə yoxlaması alınmadı. Bu tətbiq səhifə ölçüsünə uyğun rejimdən istifadə edilməklə işlədiləcək. Ən yaxşı uyğunluq üçün tətbiqi 16 KB dəstəyi ilə yenidən tərtib edin. Ətraflı məlumat üçün nəzər salın: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Bu tətbiq 16 KB ilə uyğunlaşmır. ELF düzləndirmə yoxlaması alınmadı. Bu tətbiq səhifə ölçüsünə uyğun rejimdən istifadə edilməklə işlədiləcək. Ən yaxşı uyğunluq üçün tətbiqi 16 KB dəstəyi ilə yenidən tərtib edin. Ətraflı məlumat üçün nəzər salın: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Bu tətbiq 16 KB ilə uyğunlaşmır. APK və ELF düzləndirmə yoxlamaları uğursuz oldu. Bu tətbiq səhifə ölçüsünə uyğun rejimdən istifadə edilməklə işlədiləcək. Ən yaxşı uyğunluq üçün tətbiqi 16 KB dəstəyi ilə yenidən tərtib edin. Ətraflı məlumat üçün nəzər salın: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Xidmət təmin edilməyib."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Çağrı kimliyi ayarını dəyişə bilməzsiniz."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Data <xliff:g id="CARRIERDISPLAY">%s</xliff:g> operatoruna keçirilib"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> s ərzində"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> g ərzində"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> il ərzində"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# dəqiqə əvvəl}other{# dəqiqə əvvəl}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# saat əvvəl}other{# saat əvvəl}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# gün əvvəl}other{# gün əvvəl}}"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 645239d..911eede 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ID pozivaoca je podrazumevano ograničen. Sledeći poziv: Nije ograničen."</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ID pozivaoca podrazumevano nije ograničen. Sledeći poziv: ograničen."</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID pozivaoca podrazumevano nije ograničen. Sledeći poziv: Nije ograničen."</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Ova aplikacija nije kompatibilna sa veličinom stranice od 16 kB. Provera usklađenosti APK-a nije uspela. Ova aplikacija će raditi pomoću režima kompatibilnog sa veličinom stranice. Da biste obezbedili najbolju kompatibilnost, ponovo kompajlirajte aplikaciju sa podrškom za veličinu stranice od 16 kB. Više informacija potražite na <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Ova aplikacija nije kompatibilna sa veličinom stranice od 16 kB. Provera usklađenosti ELF-a nije uspela. Ova aplikacija će raditi pomoću režima kompatibilnog sa veličinom stranice. Da biste obezbedili najbolju kompatibilnost, ponovo kompajlirajte aplikaciju sa podrškom za veličinu stranice od 16 kB. Više informacija potražite na <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Ova aplikacija nije kompatibilna sa veličinom stranice od 16 kB. Provere usklađenosti APK-a i ELF-a nisu uspele. Ova aplikacija će raditi pomoću režima kompatibilnog sa veličinom stranice. Da biste obezbedili najbolju kompatibilnost, ponovo kompajlirajte aplikaciju sa podrškom za veličinu stranice od 16 kB. Više informacija potražite na <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Usluga nije dobavljena."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Ne možete da promenite podešavanje ID-a korisnika."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Mobilni podaci su prebačeni na operatera <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"za <xliff:g id="COUNT">%d</xliff:g> s"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"za <xliff:g id="COUNT">%d</xliff:g> god"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Pre # minut}one{Pre # minut}few{Pre # minuta}other{Pre # minuta}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Pre # sat}one{Pre # sat}few{Pre # sata}other{Pre # sati}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Pre # dan}one{Pre # dan}few{Pre # dana}other{Pre # dana}}"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 7fce298..0165929 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -73,12 +73,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Ідэнтыфікатар АВН па змаўчанні абмежаваны. Наступны выклік: не абмежавана"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Па змаўчанні ідэнтыфікатар АВН не абмежаваны. Наступны выклік: абмежаваны"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Налады ідэнтыфікатару АВН па змаўчанні: не абмяжавана. Наступны выклік: не абмежавана"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Гэта праграма несумяшчальная з памерам 16 КБ. Не ўдалося праверыць выраўноўванне APK. Гэта праграма будзе працаваць у рэжыме, сумяшчальным з памерам старонкі. Для найлепшай сумяшчальнасці выканайце паўторнае кампіляванне праграмы з падтрымкай 16 КБ. Дадатковую інфармацыю можна знайсці на старонцы <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Гэта праграма несумяшчальная з памерам 16 КБ. Не ўдалося праверыць выраўноўванне ELF. Гэта праграма будзе працаваць у рэжыме, сумяшчальным з памерам старонкі. Для найлепшай сумяшчальнасці выканайце паўторнае кампіляванне праграмы з падтрымкай 16 КБ. Дадатковую інфармацыю можна знайсці на старонцы <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Гэта праграма несумяшчальная з памерам 16 КБ. Не ўдалося праверыць выраўноўванне APK і ELF. Гэта праграма будзе працаваць у рэжыме, сумяшчальным з памерам старонкі. Для найлепшай сумяшчальнасці выканайце паўторнае кампіляванне праграмы з падтрымкай 16 КБ. Дадатковую інфармацыю можна знайсці на старонцы <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Служба не прадастаўляецца."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Вы не можаце змяніць налады ідэнтыфікатара абанента, якi тэлефануе."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Мабільны трафік пераключаны на аператара \"<xliff:g id="CARRIERDISPLAY">%s</xliff:g>\""</string>
@@ -1163,6 +1160,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"праз <xliff:g id="COUNT">%d</xliff:g> гадз"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"праз <xliff:g id="COUNT">%d</xliff:g> сут"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"праз <xliff:g id="COUNT">%d</xliff:g> г."</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# хвіліну таму}one{# хвіліну таму}few{# хвіліны таму}many{# хвілін таму}other{# хвіліны таму}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# гадзіну таму}one{# гадзіну таму}few{# гадзіны таму}many{# гадзін таму}other{# гадзіны таму}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# дзень таму}one{# дзень таму}few{# дні таму}many{# дзён таму}other{# дня таму}}"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index aa03a69..1b68675 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Стандартната идентификация на повикванията е „забранено“. За следващото обаждане тя е разрешена."</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Стандартната идентификация на повикванията е „разрешено“. За следващото обаждане тя е забранена."</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Стандартната идентификация на повикванията е „разрешено“. За следващото обаждане тя е разрешена."</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Това приложение не е съвместимо със страници с размер 16 KB. Проверката за съответствие с APK файла не бе успешна. Това приложение ще се изпълнява в режим на съвместимост с размера на страницата. За най-добра съвместимост компилирайте отново приложението така, че да поддържа страници с размер 16 KB. За повече информация вижте <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Това приложение не е съвместимо със страници с размер 16 KB. Проверката за съответствие с ELF файла не бе успешна. Това приложение ще се изпълнява в режим на съвместимост с размера на страницата. За най-добра съвместимост компилирайте отново приложението така, че да поддържа страници с размер 16 KB. За повече информация вижте <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Това приложение не е съвместимо със страници с размер 16 KB. Проверките за съответствие с APK и ELF файловете не бяха успешни. Това приложение ще се изпълнява в режим на съвместимост с размера на страницата. За най-добра съвместимост компилирайте отново приложението така, че да поддържа страници с размер 16 KB. За повече информация вижте <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Услугата не е обезпечена."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Не можете да променяте настройката за идентификация на обажданията."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Преминахте към мобилни данни от <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"след <xliff:g id="COUNT">%d</xliff:g> ч"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"след <xliff:g id="COUNT">%d</xliff:g> д"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"след <xliff:g id="COUNT">%d</xliff:g> г."</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{преди # минута}other{преди # минути}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{преди # час}other{преди # часа}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{преди # ден}other{преди # дни}}"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 84d9c20..3b832e0 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ডিফল্টরূপে কলার আইডি সীমাবদ্ধ করা থাকে৷ পরবর্তী কল: সীমাবদ্ধ নয়"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ডিফল্টরূপে কলার আইডি সীমাবদ্ধ করা থাকে না৷ পরবর্তী কল: সীমাবদ্ধ"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ডিফল্টরূপে কলার আইডি সীমাবদ্ধ করা থাকে না৷ পরবর্তী কল: সীমাবদ্ধ নয়"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"এই অ্যাপ ১৬ কেবির সাথে মানানসই নয়। APK অ্যালাইনমেন্ট চেক করা যায়নি। এই অ্যাপ পৃষ্ঠার সাইজের সাথে মানানসই মোড ব্যবহার করে চলবে। সবচেয়ে ভালো মানানসই হিসেবে কাজ করার জন্য অ্যাপ্লিকেশনকে ১৬ কেবি সাপোর্টের সাথে আবার কম্পাইল করুন। আরও তথ্যের জন্য, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> দেখুন"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"এই অ্যাপ ১৬ কেবির সাথে মানানসই নয়। ELF অ্যালাইনমেন্ট চেক করা যায়নি। এই অ্যাপ পৃষ্ঠার সাইজের সাথে মানানসই মোড ব্যবহার করে চলবে। সবচেয়ে ভালো মানানসই হিসেবে কাজ করার জন্য অ্যাপ্লিকেশনকে ১৬ কেবি সাপোর্টের সাথে আবার কম্পাইল করুন। আরও তথ্যের জন্য, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> দেখুন"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"এই অ্যাপ ১৬ কেবির সাথে মানানসই নয়। APK ও ELF অ্যালাইনমেন্ট পরীক্ষা করা যায়নি। এই অ্যাপ পৃষ্ঠার সাইজের সাথে মানানসই মোড ব্যবহার করে চলবে। সবচেয়ে ভালো মানানসই হিসেবে কাজ করার জন্য অ্যাপ্লিকেশনকে ১৬ কেবি সাপোর্টের সাথে আবার কম্পাইল করুন। আরও তথ্যের জন্য, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> দেখুন"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"পরিষেবা প্রস্তুত নয়৷"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"আপনি কলার আইডি এর সেটিংস পরিবর্তন করতে পারবেন না৷"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g>-এর ডেটা ব্যবহার করা হয়েছে"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g>ঘণ্টার মধ্যে"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>দিনের মধ্যে"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g>বছরের মধ্যে"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# মিনিট আগে}one{# মিনিট আগে}other{# মিনিট আগে}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ঘণ্টা আগে}one{# ঘণ্টা আগে}other{# ঘণ্টা আগে}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# দিন আগে}one{# দিন আগে}other{# দিন আগে}}"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 13d4ac3..64ded2d 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Prikaz ID-a pozivaoca u zadanim postavkama zabranjen. Sljedeći poziv: nije zabranjen"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Prikaz ID-a pozivaoca u zadanim postavkama nije zabranjen. Sljedeći poziv: zabranjen"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Prikaz ID-a pozivaoca u zadanim postavkama nije zabranjen. Sljedeći poziv: nije zabranjen"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Ova aplikacija nije kompatibilna s veličinom stranice od 16 kB. Provjera poravnanja APK-a nije uspjela. Aplikacija će raditi pomoću načina rada za kompatibilnost s veličinom stranice. Za najbolju kompatibilnost ponovo kompajlirajte aplikaciju s podrškom za veličinu od 16 kB. Za više informacija pogledajte <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Ova aplikacija nije kompatibilna s veličinom stranice od 16 kB. Provjera poravnanja ELF-a nije uspjela. Aplikacija će raditi pomoću načina rada za kompatibilnost s veličinom stranice. Za najbolju kompatibilnost ponovo kompajlirajte aplikaciju s podrškom za veličinu od 16 kB. Za više informacija pogledajte <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Ova aplikacija nije kompatibilna s veličinom stranice od 16 kB. Provjere poravnanja APK-a i ELF-a nisu uspjele. Aplikacija će raditi pomoću načina rada za kompatibilnost s veličinom stranice. Za najbolju kompatibilnost ponovo kompajlirajte aplikaciju s podrškom za veličinu od 16 kB. Za više informacija pogledajte <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Uslugu nije moguće koristiti."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Ne možete promijeniti postavke ID-a pozivaoca."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Prijenos podataka usmjeravanjem na <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"za <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"za <xliff:g id="COUNT">%d</xliff:g> g"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Prije # min}one{Prije # min}few{Prije # min}other{Prije # min}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Prije # h}one{Prije # h}few{Prije # h}other{Prije # h}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Prije # dan}one{Prije # dan}few{Prije # dana}other{Prije # dana}}"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index a5287c6..d11941af3 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"El valor predeterminat de l\'identificador de trucada és restringit. Trucada següent: no restringit"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"El valor predeterminat de l\'identificador de trucada és no restringit. Trucada següent: restringit"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"El valor predeterminat de l\'identificador de trucada és no restringit. Trucada següent: no restringit"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Aquesta aplicació no és compatible amb 16 kB. No s\'ha pogut comprovar l\'alineació d\'APK. Aquesta aplicació s\'executarà en el mode compatible amb la mida de la pàgina. Per obtenir la millor compatibilitat, torna a compilar l\'aplicació amb compatibilitat de 16 kB. Per obtenir més informació, consulta <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Aquesta aplicació no és compatible amb 16 kB. No s\'ha pogut comprovar l\'alineació d\'ELF. Aquesta aplicació s\'executarà en el mode compatible amb la mida de la pàgina. Per obtenir la millor compatibilitat, torna a compilar l\'aplicació amb compatibilitat de 16 kB. Per obtenir més informació, consulta <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Aquesta aplicació no és compatible amb 16 kB. No s\'ha pogut comprovar l\'alineació d\'APK i ELF. Aquesta aplicació s\'executarà en el mode compatible amb la mida de la pàgina. Per obtenir la millor compatibilitat, torna a compilar l\'aplicació amb compatibilitat de 16 kB. Per obtenir més informació, consulta <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"No s\'ha proveït el servei."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"No pots canviar la configuració de l\'identificador de trucada."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Les dades s\'han canviat a <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"d\'aquí a <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"d\'aquí a <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"d\'aquí a <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Fa # minut}many{Fa # minuts}other{Fa # minuts}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Fa # hora}many{Fa # hores}other{Fa # hores}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Fa # dia}many{Fa # dies}other{Fa # dies}}"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index ad4df0f..50c3e6a 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -73,12 +73,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Ve výchozím nastavení je funkce ID volajícího omezena. Příští hovor: Neomezeno"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Ve výchozím nastavení není funkce ID volajícího omezena. Příští hovor: Omezeno"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Ve výchozím nastavení není funkce ID volajícího omezena. Příští hovor: Neomezeno"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Tato aplikace není kompatibilní s 16KB režimem. Kontrola zarovnání souboru APK selhala. Tato aplikace poběží v režimu kompatibilním s velikostmi stránek. Pro optimální kompatibilitu ji překompilujte s podporou 16KB režimu. Další informace najdete na stránce <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Tato aplikace není kompatibilní s 16KB režimem. Kontrola zarovnání souboru ELF selhala. Tato aplikace poběží v režimu kompatibilním s velikostmi stránek. Pro optimální kompatibilitu ji překompilujte s podporou 16KB režimu. Další informace najdete na stránce <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Tato aplikace není kompatibilní s 16KB režimem. Kontroly zarovnání souborů APK a ELF selhaly. Tato aplikace poběží v režimu kompatibilním s velikostmi stránek. Pro optimální kompatibilitu ji překompilujte s podporou 16KB režimu. Další informace najdete na stránce <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Služba není zřízena."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Nastavení ID volajícího nesmíte měnit."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Datové připojení bylo přepnuto na operátora <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1163,6 +1160,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"za <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"za <xliff:g id="COUNT">%d</xliff:g> r"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{před # minutou}few{před # minutami}many{před # minuty}other{před # minutami}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{před # hodinou}few{před # hodinami}many{před # hodiny}other{před # hodinami}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Před # dnem}few{před # dny}many{před # dne}other{před # dny}}"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index e77acd0..9bd5f70 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Standarder for opkalds-id til begrænset. Næste opkald: Ikke begrænset"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Standarder for opkalds-id til ikke begrænset. Næste opkald: Begrænset"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Standarder for opkalds-id til ikke begrænset. Næste opkald: Ikke begrænset"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Denne app er ikke kompatibel med sidestørrelser på 16 kB. Tjek af APK-afstemning mislykkedes. Denne app køres i kompatibilitetstilstand for sidestørrelse. Kompiler appen igen, så den understøtter 16 kB, for at opnå bedst mulig kompatibilitet. Få flere oplysninger på <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Denne app er ikke kompatibel med sidestørrelser på 16 kB. Tjek af ELF-afstemning mislykkedes. Denne app køres i kompatibilitetstilstand for sidestørrelse. Kompiler appen igen, så den understøtter 16 kB, for at opnå bedst mulig kompatibilitet. Få flere oplysninger på <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Denne app er ikke kompatibel med sidestørrelser på 16 kB. Tjek af APK- og ELF-afstemning mislykkedes. Denne app køres i kompatibilitetstilstand for sidestørrelse. Kompiler appen igen, så den understøtter 16 kB, for at opnå bedst mulig kompatibilitet. Få flere oplysninger på <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Tjenesten provisioneres ikke."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Du kan ikke ændre indstillingen for opkalds-id\'et."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Der blev skiftet til <xliff:g id="CARRIERDISPLAY">%s</xliff:g>-data"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"om <xliff:g id="COUNT">%d</xliff:g> t."</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"om <xliff:g id="COUNT">%d</xliff:g> d."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"om <xliff:g id="COUNT">%d</xliff:g> år"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{For # minut siden}one{For # minut siden}other{For # minutter siden}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# time siden}one{# time siden}other{# timer siden}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{1 dag siden}one{1 dag siden}other{# dag siden}}"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ac0a73b..06cadd5 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Anrufer-ID ist standardmäßig beschränkt. Nächster Anruf: Nicht beschränkt"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Anrufer-ID ist standardmäßig nicht beschränkt. Nächster Anruf: Beschränkt"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Anrufer-ID ist standardmäßig nicht beschränkt. Nächster Anruf: Nicht beschränkt"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Diese App ist nicht mit 16 KB kompatibel. Die APK-Abgleichsprüfung ist fehlgeschlagen. Diese App wird im mit der Seitengröße kompatiblen Modus ausgeführt. Für eine optimale Kompatibilität rekompiliere die App, sodass sie 16 KB unterstützt. Weitere Informationen findest du unter <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Diese App ist nicht mit 16 KB kompatibel. Die ELF-Abgleichsprüfung ist fehlgeschlagen. Diese App wird im mit der Seitengröße kompatiblen Modus ausgeführt. Für eine optimale Kompatibilität rekompiliere die App, sodass sie 16 KB unterstützt. Weitere Informationen findest du unter <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Diese App ist nicht mit 16 KB kompatibel. APK- und ELF-Abgleichsprüfungen sind fehlgeschlagen. Diese App wird im mit der Seitengröße kompatiblen Modus ausgeführt. Für eine optimale Kompatibilität rekompiliere die App, sodass sie 16 KB unterstützt. Weitere Informationen findest du unter <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Dienst nicht eingerichtet."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Du kannst die Einstellung für die Anrufer-ID nicht ändern."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Mobile Daten wurden auf <xliff:g id="CARRIERDISPLAY">%s</xliff:g> umgestellt"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"in <xliff:g id="COUNT">%d</xliff:g> Std."</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"in <xliff:g id="COUNT">%d</xliff:g> T"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"in <xliff:g id="COUNT">%d</xliff:g> J"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Vor # Minute}other{Vor # Minuten}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Vor # Stunde}other{Vor # Stunden}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Vor # Tag}other{Vor # Tagen}}"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index e0c2678..b4b70d8 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Η αναγνώριση κλήσης βρίσκεται από προεπιλογή στην \"περιορισμένη\". Επόμενη κλήση: Μη περιορισμένη"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Η αναγνώριση κλήσης βρίσκεται από προεπιλογή στην \"μη περιορισμένη\". Επόμενη κλήση: Περιορισμένη."</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Η αναγνώριση κλήσης βρίσκεται από προεπιλογή στην \"μη περιορισμένη\". Επόμενη κλήση: Μη περιορισμένη"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Αυτή η εφαρμογή δεν είναι συμβατή για 16 KB. Ο έλεγχος ευθυγράμμισης APK απέτυχε. Αυτή η εφαρμογή θα εκτελεστεί χρησιμοποιώντας τη λειτουργία συμβατότητας μεγέθους σελίδας. Για τη βέλτιστη συμβατότητα, επαναμεταγλωττίστε την εφαρμογή με υποστήριξη 16 KB. Για περισσότερες πληροφορίες, ανατρέξτε στη διεύθυνση <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Αυτή η εφαρμογή δεν είναι συμβατή για 16 KB. Ο έλεγχος ευθυγράμμισης ELF απέτυχε. Αυτή η εφαρμογή θα εκτελεστεί χρησιμοποιώντας τη λειτουργία συμβατότητας μεγέθους σελίδας. Για τη βέλτιστη συμβατότητα, επαναμεταγλωττίστε την εφαρμογή με υποστήριξη 16 KB. Για περισσότερες πληροφορίες, ανατρέξτε στη διεύθυνση <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Αυτή η εφαρμογή δεν είναι συμβατή για 16 KB. Οι έλεγχοι ευθυγράμμισης APK και ELF απέτυχαν. Αυτή η εφαρμογή θα εκτελεστεί χρησιμοποιώντας τη λειτουργία συμβατότητας μεγέθους σελίδας. Για τη βέλτιστη συμβατότητα, επαναμεταγλωττίστε την εφαρμογή με υποστήριξη 16 KB. Για περισσότερες πληροφορίες, ανατρέξτε στη διεύθυνση <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Η υπηρεσία δεν προβλέπεται."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Δεν μπορείτε να αλλάξετε τη ρύθμιση του αναγνωριστικού καλούντος."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Έγινε εναλλαγή των δεδομένων σε <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"σε <xliff:g id="COUNT">%d</xliff:g>ώ."</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"σε <xliff:g id="COUNT">%d</xliff:g>η."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"σε <xliff:g id="COUNT">%d</xliff:g>έτ."</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# λεπτό πριν}other{Πριν από # λεπτά}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Πριν από # ώρα}other{Πριν από # ώρες}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Πριν από # ημέρα}other{Πριν από # ημέρες}}"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index d17058a..58037eb 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Caller ID defaults to restricted. Next call: Not restricted"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Caller ID defaults to not restricted. Next call: Restricted"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Caller ID defaults to not restricted. Next call: Not restricted"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"This app isn\'t 16 KB compatible. APK alignment check failed. This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. For more information, see <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"This app isn\'t 16 KB compatible. ELF alignment check failed. This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. For more information, see <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"This app isn\'t 16 KB compatible. APK and ELF alignment checks failed. This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. For more information, see <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Service not provisioned."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"You can\'t change the caller ID setting."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Switched data to <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"in <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"in <xliff:g id="COUNT">%d</xliff:g>d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"in <xliff:g id="COUNT">%d</xliff:g> y"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minute ago}other{# minutes ago}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# hour ago}other{# hours ago}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# day ago}other{# days ago}}"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 3224c47..a212808 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1158,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"in <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"in <xliff:g id="COUNT">%d</xliff:g>d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"in <xliff:g id="COUNT">%d</xliff:g>y"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>m ago"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>h ago"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>d ago"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>y ago"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>hr"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>yr"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"in <xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"in <xliff:g id="COUNT">%d</xliff:g>hr"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"in <xliff:g id="COUNT">%d</xliff:g>d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"in <xliff:g id="COUNT">%d</xliff:g>yr"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>min ago"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>hr ago"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>d ago"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>yr ago"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minute ago}other{# minutes ago}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# hour ago}other{# hours ago}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# day ago}other{# days ago}}"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index ed4e5966..43b6ba6 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Caller ID defaults to restricted. Next call: Not restricted"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Caller ID defaults to not restricted. Next call: Restricted"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Caller ID defaults to not restricted. Next call: Not restricted"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"This app isn\'t 16 KB compatible. APK alignment check failed. This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. For more information, see <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"This app isn\'t 16 KB compatible. ELF alignment check failed. This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. For more information, see <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"This app isn\'t 16 KB compatible. APK and ELF alignment checks failed. This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. For more information, see <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Service not provisioned."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"You can\'t change the caller ID setting."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Switched data to <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"in <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"in <xliff:g id="COUNT">%d</xliff:g>d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"in <xliff:g id="COUNT">%d</xliff:g> y"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minute ago}other{# minutes ago}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# hour ago}other{# hours ago}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# day ago}other{# days ago}}"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index b17c07b..1a0f8e4 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Caller ID defaults to restricted. Next call: Not restricted"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Caller ID defaults to not restricted. Next call: Restricted"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Caller ID defaults to not restricted. Next call: Not restricted"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"This app isn\'t 16 KB compatible. APK alignment check failed. This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. For more information, see <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"This app isn\'t 16 KB compatible. ELF alignment check failed. This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. For more information, see <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"This app isn\'t 16 KB compatible. APK and ELF alignment checks failed. This app will be run using page size compatible mode. For best compatibility, please recompile the application with 16 KB support. For more information, see <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Service not provisioned."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"You can\'t change the caller ID setting."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Switched data to <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"in <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"in <xliff:g id="COUNT">%d</xliff:g>d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"in <xliff:g id="COUNT">%d</xliff:g> y"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minute ago}other{# minutes ago}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# hour ago}other{# hours ago}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# day ago}other{# days ago}}"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index e9f795e..3c73920 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"El Identificador de llamadas está predeterminado en restringido. Llamada siguiente: no restringido"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"El identificador de llamadas está predeterminado en no restringido. Llamada siguiente: restringida"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"El Identificador de llamadas está predeterminado en no restringido. Llamada siguiente: no restringido"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Esta app no es compatible con 16 KB. No se pudo verificar la alineación del APK. Esta app se ejecutará con el modo de compatibilidad de tamaño de página. Para obtener mejores resultados, vuelve a compilar la aplicación con la compatibilidad de 16 KB. Para obtener más información, consulta el siguiente vínculo: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Esta app no es compatible con 16 KB. No se pudo verificar la alineación de ELF. Esta app se ejecutará con el modo de compatibilidad de tamaño de página. Para obtener mejores resultados, vuelve a compilar la aplicación con la compatibilidad de 16 KB. Para obtener más información, consulta el siguiente vínculo: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Esta app no es compatible con 16 KB. No se pudieron verificar las alineaciones de APK y ELF. Esta app se ejecutará con el modo de compatibilidad de tamaño de página. Para obtener mejores resultados, vuelve a compilar la aplicación con la compatibilidad de 16 KB. Para obtener más información, consulta el siguiente vínculo: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Servicio no suministrado."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"No puedes cambiar la configuración del identificador de llamadas."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Se cambiaron los datos a <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"en <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"en <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"en <xliff:g id="COUNT">%d</xliff:g> años"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Hace # minuto}many{Hace # minutos}other{Hace # minutos}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Hace # hora}many{Hace # de horas}other{Hace # horas}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Hace # día}many{Hace # de días}other{Hace # días}}"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index b7893df..a81af9f 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"La identificación del emisor presenta el valor predeterminado de restringido. Siguiente llamada: No restringido"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"La la identificación del emisor presenta el valor predeterminado de no restringido. Siguiente llamada: Restringido"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"La identificación del emisor presenta el valor predeterminado de no restringido. Siguiente llamada: No restringido"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Esta aplicación no es compatible con el modo de 16 KB. No se ha podido comprobar la alineación del APK. Esta aplicación se ejecutará en el modo compatible con el tamaño de página. Para obtener la mejor compatibilidad, vuelve a compilar la aplicación con compatibilidad de 16 KB. Para obtener más información, consulta <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Esta aplicación no es compatible con el modo de 16 KB. No se ha podido comprobar la alineación de ELF. Esta aplicación se ejecutará en el modo compatible con el tamaño de página. Para obtener la mejor compatibilidad, vuelve a compilar la aplicación con compatibilidad de 16 KB. Para obtener más información, consulta <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Esta aplicación no es compatible con el modo de 16 KB. No se han podido comprobar las alineaciones de APK y ELF. Esta aplicación se ejecutará en el modo compatible con el tamaño de página. Para obtener la mejor compatibilidad, vuelve a compilar la aplicación con compatibilidad de 16 KB. Para obtener más información, consulta <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"El servicio no se suministra."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"No puedes modificar la identificación de emisor."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Datos cambiados a <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"en <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"en <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"en <xliff:g id="COUNT">%d</xliff:g>a"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Hace # minuto}many{Hace # minutos}other{Hace # minutos}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Hace # hora}many{Hace # horas}other{Hace # horas}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Hace # día}many{Hace # días}other{Hace # días}}"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 090d0cc..c5cadcf 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Helistaja ID vaikimisi piiratud. Järgmine kõne: pole piiratud"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Vaikimisi pole helistaja ID piiratud. Järgmine kõne: piiratud"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Helistaja ID pole vaikimisi piiratud. Järgmine kõne: pole piiratud"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"See rakendus ei ole suurusega 16 kB ühilduv. APK joonduse kontroll ebaõnnestus. Seda rakendust käitatakse lehesuurusega ühilduvas režiimis. Parima ühilduvuse tagamiseks kompileerige rakendus 16 kB toega uuesti. Lisateave: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"See rakendus ei ole suurusega 16 kB ühilduv. ELF-i joonduse kontroll ebaõnnestus. Seda rakendust käitatakse lehesuurusega ühilduvas režiimis. Parima ühilduvuse tagamiseks kompileerige rakendus 16 kB toega uuesti. Lisateave: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"See rakendus ei ole suurusega 16 kB ühilduv. APK ja ELF-i joonduse kontroll ebaõnnestus. Seda rakendust käitatakse lehesuurusega ühilduvas režiimis. Parima ühilduvuse tagamiseks kompileerige rakendus 16 kB toega uuesti. Lisateave: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Teenus pole ette valmistatud."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Helistaja ID seadet ei saa muuta."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Mobiilne andmeside lülitati operaatorile <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> h pärast"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> p pärast"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> a pärast"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minut tagasi}other{# minutit tagasi}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# tund tagasi}other{# tundi tagasi}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# päev tagasi}other{# päeva tagasi}}"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index d27a582..a0b2cc3 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Deitzailearen identitatea adierazteko zerbitzuaren balio lehenetsiak murriztapenak ezartzen ditu. Hurrengo deia: murriztapenik gabe."</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Deitzailearen identitatea zerbitzuaren balio lehenetsiak ez du murriztapenik ezartzen. Hurrengo deia: murriztapenekin."</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Deitzailearen identitatea zerbitzuaren balio lehenetsiak ez du murriztapenik ezartzen. Hurrengo deia: murriztapenik gabe."</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Aplikazio hau ez da bateragarria 16 KBko bertsioarekin. Ezin izan da egiaztatu APKren lerrokatzea. Orriaren tamainarekin bateragarria den modua erabilita exekutatuko da aplikazioa. Emaitza onenak lortzeko, konpilatu aplikazioa berriro 16 KBko bertsioarekin bateragarria izan dadin. Informazio gehiago lortzeko, joan hona: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Aplikazio hau ez da bateragarria 16 KBko bertsioarekin. Ezin izan da egiaztatu ELFaren lerrokatzea. Orriaren tamainarekin bateragarria den modua erabilita exekutatuko da aplikazioa. Emaitza onenak lortzeko, konpilatu aplikazioa berriro 16 KBko bertsioarekin bateragarria izan dadin. Informazio gehiago lortzeko, joan hona: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Aplikazio hau ez da bateragarria 16 KBko bertsioarekin. Ezin izan da egiaztatu APK eta ELF arteko lerrokatzea. Orriaren tamainarekin bateragarria den modua erabilita exekutatuko da aplikazioa. Emaitza onenak lortzeko, konpilatu aplikazioa berriro 16 KBko bertsioarekin bateragarria izan dadin. Informazio gehiago lortzeko, joan hona: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Zerbitzua ez da hornitu."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Ezin duzu aldatu deitzailearen identitatearen ezarpena."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g> operadorearen datu-konexiora aldatu zara"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> h barru"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> eg. barru"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> ur. barru"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Duela # minutu}other{Duela # minutu}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Duela # ordu}other{Duela # ordu}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Duela # egun}other{Duela # egun}}"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 50c0e37..4d3f1a3 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"پیشفرض شناسه تماسگیرنده روی محدود است. تماس بعدی: بدون محدودیت"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"پیشفرض شناسه تماسگیرنده روی غیرمحدود است. تماس بعدی: محدود"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"پیشفرض شناسه تماسگیرنده روی غیرمحدود است. تماس بعدی: بدون محدودیت"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"این برنامه با صفحه ۱۶ کیلوبایتی سازگار نیست. بررسی تراز APK ناموفق بود. این برنامه بااستفاده از حالت سازگار اندازه صفحه اجرا خواهد شد. برای بهترین سازگاری، لطفاً برنامه را با پشتیبانی از صفحه ۱۶ کیلوبایتی دوباره ترجمه کنید. برای اطلاعات بیشتر، به <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> مراجعه کنید"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"این برنامه با صفحه ۱۶ کیلوبایتی سازگار نیست. بررسی تراز ELF ناموفق بود. این برنامه بااستفاده از حالت سازگار اندازه صفحه اجرا خواهد شد. برای بهترین سازگاری، لطفاً برنامه را با پشتیبانی از صفحه ۱۶ کیلوبایتی دوباره ترجمه کنید. برای اطلاعات بیشتر، به <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> مراجعه کنید"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"این برنامه با صفحه ۱۶ کیلوبایتی سازگار نیست. بررسیهای تراز APK و ELF ناموفق بود. این برنامه بااستفاده از حالت سازگار اندازه صفحه اجرا خواهد شد. برای بهترین سازگاری، لطفاً برنامه را با پشتیبانی از صفحه ۱۶ کیلوبایتی دوباره ترجمه کنید. برای اطلاعات بیشتر، به <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> مراجعه کنید"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"سرویس دارای مجوز نیست."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"شما میتوانید تنظیم شناسه تماسگیرنده را تغییر دهید."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"داده به <xliff:g id="CARRIERDISPLAY">%s</xliff:g> تغییر کرد"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"تا <xliff:g id="COUNT">%d</xliff:g> ساعت دیگر"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"تا <xliff:g id="COUNT">%d</xliff:g> روز دیگر"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"تا <xliff:g id="COUNT">%d</xliff:g> سال دیگر"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# دقیقه قبل}one{# دقیقه قبل}other{# دقیقه قبل}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ساعت قبل}one{# ساعت قبل}other{# ساعت قبل}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# روز قبل}one{# روز قبل}other{# روز قبل}}"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 7d47541..2f57750 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Soittajan tunnukseksi muutetaan rajoitettu. Seuraava puhelu: ei rajoitettu"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Soittajan tunnukseksi muutetaan rajoittamaton. Seuraava puhelu: rajoitettu"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Soittajan tunnukseksi muutetaan rajoittamaton. Seuraava puhelu: ei rajoitettu"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Tämä sovellus ei tue 16 kt:tä. APK-sovelluksen yhteensopivuustarkistus epäonnistui. Sovellus käynnistetään sivukoon kanssa yhteensopivan tilan avulla. Parhaan yhteensopivuuden varmistamiseksi kokoa sovellus uudelleen niin, että se tukee 16 kt:tä. Katso lisätietoa osoitteesta <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Tämä sovellus ei tue 16 kt:tä. ELF:n yhteensopivuustarkastus epäonnistui. Sovellus käynnistetään sivukoon kanssa yhteensopivan tilan avulla. Parhaan yhteensopivuuden varmistamiseksi kokoa sovellus uudelleen niin, että se tukee 16 kt:tä. Katso lisätietoa osoitteesta <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Tämä sovellus ei tue 16 kt:tä. APK:n ja ELF:n yhteensopivuustarkastukset epäonnistuivat. Sovellus käynnistetään sivukoon kanssa yhteensopivan tilan avulla. Parhaan yhteensopivuuden varmistamiseksi kokoa sovellus uudelleen niin, että se tukee 16 kt:tä. Katso lisätietoa osoitteesta <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Palvelua ei tarjota."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Et voi muuttaa soittajan tunnuksen asetusta."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Data vaihdettu: <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> h:n päästä"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> pv:n päästä"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> v:n päästä"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuutti sitten}other{# minuuttia sitten}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# tunti sitten}other{# tuntia sitten}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# päivä sitten}other{# päivää sitten}}"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 685d940..a87fd67 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Par défaut, les numéros des appelants ne sont pas restreints. Appel suivant : non restreint"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Par défaut, les numéros des appelants ne sont pas restreints. Appel suivant : restreint"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Par défaut, les numéros des appelants ne sont pas restreints. Appel suivant : non restreint"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Cette appli n\'est pas compatible avec les pages de 16 ko. La vérification de l\'alignement de fichiers APK a échoué. Cette appli sera exécutée en mode compatible avec la taille de la page. Pour une meilleure compatibilité, veuillez recompiler l\'application avec la prise en charge de pages de 16 ko. Pour en savoir plus, consultez la page <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Cette appli n\'est pas compatible avec les pages de 16 ko. La vérification de l\'alignement de fichiers ELF a échoué. Cette appli sera exécutée en mode compatible avec la taille de la page. Pour une meilleure compatibilité, veuillez recompiler l\'application avec la prise en charge de pages de 16 ko. Pour en savoir plus, consultez la page <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Cette appli n\'est pas compatible avec les pages de 16 ko. Les vérifications d\'alignement de fichiers APK et ELF ont échoué. Cette appli sera exécutée en mode compatible avec la taille de la page. Pour une meilleure compatibilité, veuillez recompiler l\'application avec la prise en charge de pages de 16 ko. Pour en savoir plus, consultez la page <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Ce service n\'est pas pris en charge."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Impossible de modifier le paramètre relatif au numéro de l\'appelant."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Données changées à <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"dans <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"dans <xliff:g id="COUNT">%d</xliff:g> j"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"dans <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Il y a # minute}one{Il y a # minute}many{Il y a # minutes}other{Il y a # minutes}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Il y a # heure}one{Il y a # heure}many{Il y a # heures}other{Il y a # heures}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Il y a # jour}one{Il y a # jour}many{Il y a # jours}other{Il y a # jours}}"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index e576e92..ba09a53 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Par défaut, les numéros des appelants ne sont pas restreints. Appel suivant : non restreint"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Par défaut, les numéros des appelants ne sont pas restreints. Appel suivant : restreint"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Par défaut, les numéros des appelants ne sont pas restreints. Appel suivant : non restreint"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Cette appli n\'est pas compatible avec les pages de 16 ko. Échec de la vérification de l\'alignement de l\'APK. Cette appli sera exécutée dans un mode compatible avec la taille de la page. Pour une compatibilité optimale, veuillez recompiler l\'application de manière à ce que la taille de 16 ko soit prise en charge. Pour en savoir plus, consultez <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Cette appli n\'est pas compatible avec les pages de 16 ko. Échec de la vérification de l\'alignement de l\'ELF. Cette appli sera exécutée dans un mode compatible avec la taille de la page. Pour une compatibilité optimale, veuillez recompiler l\'application de manière à ce que la taille de 16 ko soit prise en charge. Pour en savoir plus, consultez <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Cette appli n\'est pas compatible avec les pages de 16 ko. Échec des vérifications de l\'alignement de l\'APK et de l\'ELF. Cette appli sera exécutée dans un mode compatible avec la taille de la page. Pour une compatibilité optimale, veuillez recompiler l\'application de manière à ce que la taille de 16 ko soit prise en charge. Pour en savoir plus, consultez <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Ce service n\'est pas pris en charge."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Impossible de modifier le paramètre relatif au numéro de l\'appelant."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Données transférées vers <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"dans <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"dans <xliff:g id="COUNT">%d</xliff:g> j"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"dans <xliff:g id="COUNT">%d</xliff:g> an"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Il y a # minute}one{Il y a # minute}many{Il y a # minutes}other{Il y a # minutes}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Il y a # heure}one{Il y a # heure}many{Il y a # heures}other{Il y a # heures}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Il y a # jour}one{Il y a # jour}many{Il y a # jours}other{Il y a # jours}}"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index f2354f5..c46436f 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"O valor predeterminado do identificador de chamada é restrinxido. Próxima chamada: non restrinxido"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"O valor predeterminado do identificador de chamada é non restrinxido. Próxima chamada: restrinxido"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"O valor predeterminado do identificador de chamada é restrinxido. Próxima chamada: non restrinxido"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Esta aplicación non é compatible con 16 kB. Produciuse un erro ao verificar o aliñamento de ficheiros APK. Esta aplicación executarase usando o modo compatible co tamaño de páxina. Para obter a mellor compatibilidade, volve compilar a aplicación con asistencia de 16 kB. Se precisas máis información, atoparala en <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Esta aplicación non é compatible con 16 kB. Produciuse un erro ao verificar o aliñamento de ficheiros ELF. Esta aplicación executarase usando o modo compatible co tamaño de páxina. Para obter a mellor compatibilidade, volve compilar a aplicación con asistencia de 16 kB. Se precisas máis información, atoparala en <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Esta aplicación non é compatible con 16 kB. Produciuse un erro ao verificar o aliñamento de ficheiros APK e ELF. Esta aplicación executarase usando o modo compatible co tamaño de páxina. Para obter a mellor compatibilidade, volve compilar a aplicación con asistencia de 16 kB. Se precisas máis información, atoparala en <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Servizo non ofrecido."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Non podes cambiar a configuración do identificador de chamada."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Cambiouse a conexión de datos a <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"en <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"en <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"en <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Hai # minuto}other{Hai # minutos}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Hai # hora}other{Hai # horas}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Hai # día}other{Hai # días}}"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 6291688..89f5a59 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"કૉલર ID પ્રતિબંધિત પર ડિફોલ્ટ છે. આગલો કૉલ: પ્રતિબંધિત નહીં"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"કૉલર ID પ્રતિબંધિત નહીં પર ડિફોલ્ટ છે. આગલો કૉલ: પ્રતિબંધિત"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"કૉલર ID પ્રતિબંધિત નહીં પર ડિફોલ્ટ છે. આગલો કૉલ: પ્રતિબંધિત નહીં"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"આ ઍપ 16 KB સુસંગત નથી. APK સંરેખણની તપાસ નિષ્ફળ રહી. આ ઍપ પેજના કદ સાથે સુસંગત મોડનો ઉપયોગ કરીને ચાલશે. શ્રેષ્ઠ સુસંગતતા માટે, કૃપા કરીને 16 KB સપોર્ટવાળી ઍપ્લિકેશન ફરીથી સંકલિત કરો. વધુ માહિતી માટે, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> જુઓ"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"આ ઍપ 16 KB સુસંગત નથી. ELF સંરેખણની તપાસ નિષ્ફળ રહી. આ ઍપ પેજના કદ સાથે સુસંગત મોડનો ઉપયોગ કરીને ચાલશે. શ્રેષ્ઠ સુસંગતતા માટે, કૃપા કરીને 16 KB સપોર્ટવાળી ઍપ્લિકેશન ફરીથી સંકલિત કરો. વધુ માહિતી માટે, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> જુઓ"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"આ ઍપ 16 KB સુસંગત નથી. APK અને ELF સંરેખણની તપાસ નિષ્ફળ રહી. આ ઍપ પેજના કદ સાથે સુસંગત મોડનો ઉપયોગ કરીને ચાલશે. શ્રેષ્ઠ સુસંગતતા માટે, કૃપા કરીને 16 KB સપોર્ટવાળી ઍપ્લિકેશન ફરીથી સંકલિત કરો. વધુ માહિતી માટે, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> જુઓ"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"સેવાની જોગવાઈ કરી નથી."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"તમે કૉલર ID સેટિંગ બદલી શકતાં નથી."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"ડેટા <xliff:g id="CARRIERDISPLAY">%s</xliff:g> પર સ્વિચ કર્યો"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> કલાકમાં"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> દિવસમાં"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> વર્ષમાં"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# મિનિટ પહેલાં}one{# મિનિટ પહેલાં}other{# મિનિટ પહેલાં}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# કલાક પહેલાં}one{# કલાક પહેલાં}other{# કલાક પહેલાં}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# દિવસ પહેલાં}one{# દિવસ પહેલાં}other{# દિવસ પહેલાં}}"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 75c8bd6..4bdfa6b 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"कॉलर आईडी डिफ़ॉल्ट रूप से सीमित है. अगली कॉल: सीमित नहीं"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"कॉलर आईडी डिफ़ॉल्ट रूप से सीमित नहीं है. अगली कॉल: सीमित"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"कॉलर आईडी डिफ़ॉल्ट रूप से सीमित नहीं है. अगली कॉल: सीमित नहीं"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"यह ऐप्लिकेशन 16 केबी वाले पेजों के साथ काम नहीं करता. APK के अलाइनमेंट की जांच नहीं की जा सकी. यह ऐप्लिकेशन, पेज साइज़ के साथ काम करने वाले मोड का इस्तेमाल करके चलेगा. पेज साइज़ के साथ बेहतर तरीके से काम के लिए, कृपया 16 केबी वाले पेजों के साथ ऐप्लिकेशन को फिर से कंपाइल करें. ज़्यादा जानकारी के लिए, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> पर जाएं"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"यह ऐप्लिकेशन 16 केबी वाले पेजों के साथ काम नहीं करता. ईएलएफ़ अलाइनमेंट की जांच नहीं की जा सकी. यह ऐप्लिकेशन, पेज साइज़ के साथ काम करने वाले मोड का इस्तेमाल करके चलेगा. पेज साइज़ के साथ बेहतर तरीके से काम के लिए, कृपया 16 केबी वाले पेजों के साथ ऐप्लिकेशन को फिर से कंपाइल करें. ज़्यादा जानकारी के लिए, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> पर जाएं"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"यह ऐप्लिकेशन 16 केबी वाले पेजों के साथ काम नहीं करता. APK और ईएलएफ़ के अलाइनमेंट की जांच नहीं की जा सकी. यह ऐप्लिकेशन, पेज साइज़ के साथ काम करने वाले मोड का इस्तेमाल करके चलेगा. पेज साइज़ के साथ बेहतर तरीके से काम के लिए, कृपया 16 केबी वाले पेजों के साथ ऐप्लिकेशन को फिर से कंपाइल करें. ज़्यादा जानकारी के लिए, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> पर जाएं"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"सेवा प्रावधान की हुई नहीं है."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"आप कॉलर आईडी सेटिंग नहीं बदल सकते."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"डेटा को <xliff:g id="CARRIERDISPLAY">%s</xliff:g> पर स्विच किया गया"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> घंटे में"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> दिन में"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> साल में"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# मिनट पहले}one{# मिनट पहले}other{# मिनट पहले}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# घंटा पहले}one{# घंटा पहले}other{# घंटे पहले}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# दिन पहले}one{# दिन पहले}other{# दिन पहले}}"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 0516c46..cd7d023 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Zadana postavka ID-a pozivatelja ima ograničenje. Sljedeći poziv: Nije ograničen"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Zadana postavka ID-a pozivatelja nema ograničenje. Sljedeći poziv: Ograničen"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Zadana postavka ID-a pozivatelja nema ograničenje. Sljedeći poziv: Nije ograničen"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Ova aplikacija nije kompatibilna s veličinom stranice od 16 KB. Provjera poravnanja APK-a nije uspjela. Ova će se aplikacija pokrenuti pomoću načina kompatibilnog s veličinom stranice. Za najbolju kompatibilnost ponovo kompilirajte aplikaciju s podrškom od 16 KB. Više informacija potražite na stranici <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Ova aplikacija nije kompatibilna s veličinom stranice od 16 KB. Provjera poravnanja ELF-a nije uspjela. Ova će se aplikacija pokrenuti pomoću načina kompatibilnog s veličinom stranice. Za najbolju kompatibilnost ponovo kompilirajte aplikaciju s podrškom od 16 KB. Više informacija potražite na stranici <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Ova aplikacija nije kompatibilna s veličinom stranice od 16 KB. Provjere poravnanja APK-a i ELF-a nisu uspjele. Ova će se aplikacija pokrenuti pomoću načina kompatibilnog s veličinom stranice. Za najbolju kompatibilnost ponovo kompilirajte aplikaciju s podrškom od 16 KB. Više informacija potražite na stranici <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Usluga nije rezervirana."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Ne možete promijeniti postavku ID-a pozivatelja."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Podaci su prebačeni na <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"za <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"za <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"za <xliff:g id="COUNT">%d</xliff:g> g."</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Prije # min}one{Prije # min}few{Prije # min}other{Prije # min}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Prije # h}one{Prije # h}few{Prije # h}other{Prije # h}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Prije # dan}one{Prije # dan}few{Prije # dana}other{Prije # dana}}"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index e980141..ddc8762 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"A hívóazonosító alapértelmezett értéke korlátozott. Következő hívás: nem korlátozott"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"A hívóazonosító alapértelmezett értéke nem korlátozott. Következő hívás: korlátozott"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"A hívóazonosító alapértelmezett értéke nem korlátozott. Következő hívás: nem korlátozott"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Ez az alkalmazás nem kompatibilis a 16 kB-os mérettel. Az APK-igazítási ellenőrzés sikertelen volt. Ez az app az oldalméret-kompatibilis mód használatával fog futni. A legjobb kompatibilitás érdekében fordítsa újra az alkalmazást 16 kB-os támogatással. További információ: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Ez az alkalmazás nem kompatibilis a 16 kB-os mérettel. Az ELF-igazítási ellenőrzés sikertelen. Ez az app az oldalméret-kompatibilis mód használatával fog futni. A legjobb kompatibilitás érdekében fordítsa újra az alkalmazást 16 kB-os támogatással. További információ: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Ez az alkalmazás nem kompatibilis a 16 kB-os mérettel. Az APK- és ELF-igazítási ellenőrzések sikertelenek voltak. Ez az app az oldalméret-kompatibilis mód használatával fog futni. A legjobb kompatibilitás érdekében fordítsa újra az alkalmazást 16 kB-os támogatással. További információ: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"A szolgáltatás nincs biztosítva."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Nem tudja módosítani a hívó fél azonosítója beállítást."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Adatforgalom átváltva a következőre: <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> ó múlva"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> n múlva"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> é múlva"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# perce}other{# perce}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# órája}other{# órája}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# napja}other{# napja}}"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index f734a5e..3e961e7 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Զանգողի ID-ն լռելյայն սահմանափակված է: Հաջորդ զանգը` չսահմանափակված"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Զանգողի ID-ն լռելյայն չսահմանափակված է: Հաջորդ զանգը` Սահմանափակված"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Զանգողի ID-ն լռելյայն չսահմանափակված է: Հաջորդ զանգը` չսահմանափակված"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Այս հավելվածը հարմարեցված չէ 16 ԿԲ չափի համար։ APK-ի հետ համատեղելիությունը ձախողվել է։ Այս հավելվածը կաշխատի էջի չափի հետ համատեղելի ռեժիմում։ Համատեղելիությունն ապահովելու համար նորից կոմպիլացրեք հավելվածը 16 ԿԲ չափն աջակցելու համար։ Լրացուցիչ տեղեկություններ ստանալու համար այցելեք <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Այս հավելվածը հարմարեցված չէ 16 ԿԲ չափի համար։ ELF-ի հետ համատեղելիությունը ձախողվել է։ Այս հավելվածը կաշխատի էջի չափի հետ համատեղելի ռեժիմում։ Համատեղելիությունն ապահովելու համար նորից կոմպիլացրեք հավելվածը 16 ԿԲ չափն աջակցելու համար։ Լրացուցիչ տեղեկություններ ստանալու համար այցելեք <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Այս հավելվածը հարմարեցված չէ 16 ԿԲ չափի համար։ APK-ի և ELF-ի հետ համատեղելիության ստուգումը ձախողվել է։ Այս հավելվածը կաշխատի էջի չափի հետ համատեղելի ռեժիմում։ Համատեղելիությունն ապահովելու համար նորից կոմպիլացրեք հավելվածը 16 ԿԲ չափն աջակցելու համար։ Լրացուցիչ տեղեկություններ ստանալու համար այցելեք <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Ծառայությունը չի տրամադրվում:"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Դուք չեք կարող փոխել զանգողի ID-ի կարգավորումները:"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Օգտագործվում է <xliff:g id="CARRIERDISPLAY">%s</xliff:g>-ի բջջային ինտերնետը"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> ժամից"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> օրից"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> տարուց"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# րոպե առաջ}one{# րոպե առաջ}other{# րոպե առաջ}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ժամ առաջ}one{# ժամ առաջ}other{# ժամ առաջ}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# օր առաջ}one{# օր առաջ}other{# օր առաջ}}"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 3312662..594c173 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ID penelepon diatur default ke \"dibatasi\". Panggilan selanjutnya: Tidak dibatasi."</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ID penelepon diatur default ke tidak dibatasi. Panggilan selanjutnya: Dibatasi"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID penelepon diatur default ke tidak dibatasi. Panggilan selanjutnya: Tidak dibatasi"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Aplikasi ini tidak kompatibel dengan 16 KB. Pemeriksaan keselarasan APK gagal. Aplikasi ini akan dijalankan menggunakan mode yang kompatibel dengan ukuran halaman. Untuk kompatibilitas terbaik, kompilasi ulang aplikasi dengan dukungan 16 KB. Untuk mengetahui informasi selengkapnya, lihat <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Aplikasi ini tidak kompatibel dengan 16 KB. Pemeriksaan keselarasan ELF gagal. Aplikasi ini akan dijalankan menggunakan mode yang kompatibel dengan ukuran halaman. Untuk kompatibilitas terbaik, kompilasi ulang aplikasi dengan dukungan 16 KB. Untuk mengetahui informasi selengkapnya, lihat <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Aplikasi ini tidak kompatibel dengan 16 KB. Pemeriksaan keselarasan APK dan ELF gagal. Aplikasi ini akan dijalankan menggunakan mode yang kompatibel dengan ukuran halaman. Untuk kompatibilitas terbaik, kompilasi ulang aplikasi dengan dukungan 16 KB. Untuk mengetahui informasi selengkapnya, lihat <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Layanan tidak diperlengkapi."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Anda tidak dapat mengubah setelan ID penelepon."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Mengalihkan data seluler ke <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"dalam <xliff:g id="COUNT">%d</xliff:g> j"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"dalam <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"dalam <xliff:g id="COUNT">%d</xliff:g> t"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# menit lalu}other{# menit lalu}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# jam lalu}other{# jam lalu}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# hari lalu}other{# hari lalu}}"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index cbf9798..0242fa3 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1158,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"eftir <xliff:g id="COUNT">%d</xliff:g> klst."</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"eftir <xliff:g id="COUNT">%d</xliff:g> d."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"eftir <xliff:g id="COUNT">%d</xliff:g> ár"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Fyrir # mínútu}one{Fyrir # mínútu}other{Fyrir # mínútum}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Fyrir # klukkustund}one{Fyrir # klukkustund}other{Fyrir # klukkustundum}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Fyrir # degi}one{Fyrir # degi}other{Fyrir # dögum}}"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 747212a..e912417 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1159,6 +1159,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"tra <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"tra <xliff:g id="COUNT">%d</xliff:g> g"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"tra <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuto fa}many{# di minuti fa}other{# minuti fa}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ora fa}many{# di ore fa}other{# ore fa}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# giorno fa}many{# di giorni fa}other{# giorni fa}}"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index afdd157..a9ce2d2 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"שירות השיחה המזוהה עובר כברירת מחדל למצב מוגבל. השיחה הבאה: לא מוגבלת"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"שירות \'שיחה מזוהה\' עובר כברירת מחדל למצב לא מוגבל. השיחה הבאה: מוגבלת"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"זיהוי מתקשר עובר כברירת מחדל למצב לא מוגבל. השיחה הבאה: לא מוגבלת"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"האפליקציה הזו לא תואמת לדפים בגודל 16KB. בדיקות ההתאמה ל-APK נכשלה. האפליקציה הזו תופעל במצב תואם לגודל הדף. כדי לקבל את התאימות הטובה ביותר, צריך להדר מחדש (recompile) את האפליקציה לתמיכה בדפים בגודל 16KB. מידע נוסף זמין בכתובת <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"האפליקציה הזו לא תואמת לדפים בגודל 16KB. בדיקות ההתאמה ל-ELF נכשלה. האפליקציה הזו תופעל במצב תואם לגודל הדף. כדי לקבל את התאימות הטובה ביותר, צריך להדר מחדש (recompile) את האפליקציה לתמיכה בדפים בגודל 16KB. מידע נוסף זמין בכתובת <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"האפליקציה הזו לא תואמת לדפים בגודל 16KB. בדיקות ההתאמה ל-APK ול-ELF נכשלו. האפליקציה הזו תופעל במצב תואם לגודל הדף. כדי לקבל את התאימות הטובה ביותר, צריך להדר מחדש (recompile) את האפליקציה לתמיכה בדפים בגודל 16KB. מידע נוסף זמין בכתובת <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"השירות לא הוקצה."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"אינך יכול לשנות את הגדרת זיהוי המתקשר."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"הנתונים עברו אל <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"בעוד <xliff:g id="COUNT">%d</xliff:g> שע‘"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"בעוד <xliff:g id="COUNT">%d</xliff:g> י‘"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"בעוד <xliff:g id="COUNT">%d</xliff:g> שנים"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{לפני דקה}one{לפני # דקות}two{לפני # דקות}other{לפני # דקות}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{לפני שעה}one{לפני # שעות}two{לפני שעתיים}other{לפני # שעות}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{לפני יום}one{לפני # ימים}two{לפני יומיים}other{לפני # ימים}}"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 5139e2a..3d7c91c 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1158,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> 時間後"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> 日後"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> 年後"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> 分前"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> 時間前"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> 日前"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> 年前"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> 分"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> 時間"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> 日"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> 年"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"あと <xliff:g id="COUNT">%d</xliff:g> 分"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"あと <xliff:g id="COUNT">%d</xliff:g> 時間"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"あと <xliff:g id="COUNT">%d</xliff:g> 日"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"あと <xliff:g id="COUNT">%d</xliff:g> 年"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> 分前"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> 時間前"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> 日前"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> 年前"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# 分前}other{# 分前}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# 時間前}other{# 時間前}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# 日前}other{# 日前}}"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 1a0ba5a..b52896b 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1158,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> საათში"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> დღეში"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> წელში"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# წუთის წინ}other{# წუთის წინ}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# საათის წინ}other{# საათის წინ}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# დღის წინ}other{# დღის წინ}}"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index fd235c2..49b4bfc 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Қоңырау шалушының жеке анықтағышы бастапқы бойынша шектелген. Келесі қоңырау: Шектелмеген"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Қоңырау шалушының жеке анықтағышы бастапқы бойынша шектелмеген. Келесі қоңырау: Шектелген"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Қоңырау шалушының жеке анықтағышы бастапқы бойынша шектелмеген. Келесі қоңырау: Шектелмеген"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Бұл қолданба 16 КБ көлеміне қолдау көрсетпейді. APK туралау тексерісі орындалмады. Бұл қолданба бет өлшемімен сәйкестік режимінде іске қосылады. Сәйкестікті жақсарту үшін қолданбаны 16 КБ көлемін қолдайтындай етіп қайта құрастырыңыз. Қосымша ақпарат алу үшін <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> сілтемесін қарап шығыңыз."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Бұл қолданба 16 КБ көлеміне қолдау көрсетпейді. ELF туралау тексерісі орындалмады. Бұл қолданба бет өлшемімен сәйкестік режимінде іске қосылады. Сәйкестікті жақсарту үшін қолданбаны 16 КБ көлемін қолдайтындай етіп қайта құрастырыңыз. Қосымша ақпарат алу үшін <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> сілтемесін қарап шығыңыз."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Бұл қолданба 16 КБ көлеміне қолдау көрсетпейді. APK және ELF файлдарының туралау тексерісі орындалмады. Бұл қолданба бет өлшемімен сәйкестік режимінде іске қосылады. Сәйкестікті жақсарту үшін қолданбаны 16 КБ көлемін қолдайтындай етіп қайта құрастырыңыз. Қосымша ақпарат алу үшін <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> сілтемесін қарап шығыңыз."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Қызмет ұсынылмаған."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Қоңырау шалушы идентификаторы параметрін өзгерту мүмкін емес."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Деректер <xliff:g id="CARRIERDISPLAY">%s</xliff:g> операторына ауыстырылды"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> сағ кейін"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> күннен кейін"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> жылдан кейін"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> мин бұрын"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> сағ бұрын"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> күн бұрын"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> жыл бұрын"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> мин"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> сағ"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> күн"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> жыл"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> минуттан кейін"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> сағаттан кейін"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> күннен кейін"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> жылдан кейін"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> мин бұрын"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> сағ бұрын"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> күн бұрын"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> жыл бұрын"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# минут бұрын}other{# минут бұрын}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# сағат бұрын}other{# сағат бұрын}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# күн бұрын}other{# күн бұрын}}"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 2fddb5f..27299c5 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"មិនបានដាក់កម្រិតលំនាំដើមលេខសម្គាល់អ្នកហៅ។ ការហៅបន្ទាប់៖ មិនបានដាក់កម្រិត។"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"មិនបានដាក់កម្រិតលេខសម្គាល់អ្នកហៅលំនាំដើម។ ការហៅបន្ទាប់៖ បានដាក់កម្រិត"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"មិនបានដាក់កម្រិតលំនាំដើមលេខសម្គាល់អ្នកហៅ។ ការហៅបន្ទាប់៖ មិនបានដាក់កម្រិត។"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"កម្មវិធីនេះមិនត្រូវគ្នានឹង 16 KB ទេ។ ការត្រួតពិនិត្យការតម្រឹម APK មិនបានសម្រេចទេ។ កម្មវិធីនេះនឹងត្រូវបានដំណើរការដោយប្រើមុខងារដែលត្រូវគ្នានឹងទំហំទំព័រ។ ដើម្បីទទួលបានភាពត្រូវគ្នាល្អបំផុត សូមចងក្រងកម្មវិធីឡើងវិញដោយប្រើជំនួយ 16 KB។ ដើម្បីទទួលបានព័ត៌មានបន្ថែម សូមមើល <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"កម្មវិធីនេះមិនត្រូវគ្នានឹង 16 KB ទេ។ ការត្រួតពិនិត្យការតម្រឹម ELF មិនបានសម្រេចទេ។ កម្មវិធីនេះនឹងត្រូវបានដំណើរការដោយប្រើមុខងារដែលត្រូវគ្នានឹងទំហំទំព័រ។ ដើម្បីទទួលបានភាពត្រូវគ្នាល្អបំផុត សូមចងក្រងកម្មវិធីឡើងវិញដោយប្រើជំនួយ 16 KB។ ដើម្បីទទួលបានព័ត៌មានបន្ថែម សូមមើល <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"កម្មវិធីនេះមិនត្រូវគ្នានឹង 16 KB ទេ។ ការត្រួតពិនិត្យការតម្រឹម APK និង ELF មិនបានសម្រេចទេ។ កម្មវិធីនេះនឹងត្រូវបានដំណើរការដោយប្រើមុខងារដែលត្រូវគ្នានឹងទំហំទំព័រ។ ដើម្បីទទួលបានភាពត្រូវគ្នាល្អបំផុត សូមចងក្រងកម្មវិធីឡើងវិញដោយប្រើជំនួយ 16 KB។ ដើម្បីទទួលបានព័ត៌មានបន្ថែម សូមមើល <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"មិនបានផ្ដល់សេវាកម្ម។"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"អ្នកមិនអាចប្ដូរការកំណត់លេខសម្គាល់អ្នកហៅបានទេ។"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"បានប្ដូរទិន្នន័យទៅ <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"ក្នុងរយៈពេល <xliff:g id="COUNT">%d</xliff:g>ម៉"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"ក្នុងរយៈពេល <xliff:g id="COUNT">%d</xliff:g>ថ"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"ក្នុងរយៈពេល <xliff:g id="COUNT">%d</xliff:g>ឆ"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# នាទីមុន}other{# នាទីមុន}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ម៉ោងមុន}other{# ម៉ោងមុន}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ថ្ងៃមុន}other{# ថ្ងៃមុន}}"</string>
@@ -1224,7 +1253,7 @@
<string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{ផ្កាយមួយក្នុងចំណោមផ្កាយ {max}}other{ផ្កាយ # ក្នុងចំណោមផ្កាយ {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"កំពុងដំណើរការ"</string>
<string name="whichApplication" msgid="5432266899591255759">"បញ្ចប់សកម្មភាពដោយប្រើ"</string>
- <string name="whichApplicationNamed" msgid="6969946041713975681">"បញ្ចប់សកម្មភាពដោយប្រើ %%1$s"</string>
+ <string name="whichApplicationNamed" msgid="6969946041713975681">"បញ្ចប់សកម្មភាពដោយប្រើ %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"បញ្ចប់សកម្មភាព"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"បើកជាមួយ"</string>
<string name="whichViewApplicationNamed" msgid="415164730629690105">"បើកជាមួយ %1$s"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 4264717..ad62f42 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -561,13 +561,13 @@
<string name="permdesc_transmitIr" product="tablet" msgid="5884738958581810253">"ಟ್ಯಾಬ್ಲೆಟ್ನ ಇನ್ಫ್ರಾರೆಡ್ ಸಂವಾಹಕವನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permdesc_transmitIr" product="tv" msgid="3278506969529173281">"ನಿಮ್ಮ Android TV ಸಾಧನದ ಇನ್ಫ್ರಾರೆಡ್ ಟ್ರಾನ್ಸ್ಮೀಟರ್ ಅನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permdesc_transmitIr" product="default" msgid="8484193849295581808">"ಫೋನ್ನ ಇನ್ಫ್ರಾರೆಡ್ ಸಂವಾಹಕವನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
- <string name="permlab_setWallpaper" msgid="6959514622698794511">"ವಾಲ್ಪೇಪರ್ ಹೊಂದಿಸಿ"</string>
+ <string name="permlab_setWallpaper" msgid="6959514622698794511">"ವಾಲ್ಪೇಪರ್ ಸೆಟ್ ಮಾಡಿ"</string>
<string name="permdesc_setWallpaper" msgid="2973996714129021397">"ಸಿಸ್ಟಂ ವಾಲ್ಪೇಪರ್ ಹೊಂದಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_accessHiddenProfile" msgid="8607094418491556823">"ಮರೆಮಾಡಲಾದ ಪ್ರೊಫೈಲ್ಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಿ"</string>
<string name="permdesc_accessHiddenProfile" msgid="1543153202481009676">"ಮರೆಮಾಡಲಾದ ಪ್ರೊಫೈಲ್ಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_setWallpaperHints" msgid="1153485176642032714">"ನಿಮ್ಮ ವಾಲ್ಪೇಪರ್ ಗಾತ್ರವನ್ನು ಸರಿಹೊಂದಿಸಿ"</string>
<string name="permdesc_setWallpaperHints" msgid="6257053376990044668">"ಸಿಸ್ಟಂ ವಾಲ್ಪೇಪರ್ ಗಾತ್ರದ ಸುಳಿವುಗಳನ್ನು ಹೊಂದಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
- <string name="permlab_setTimeZone" msgid="7922618798611542432">"ಸಮಯದ ವಲಯವನ್ನು ಹೊಂದಿಸಿ"</string>
+ <string name="permlab_setTimeZone" msgid="7922618798611542432">"ಸಮಯದ ವಲಯವನ್ನು ಸೆಟ್ ಮಾಡಿ"</string>
<string name="permdesc_setTimeZone" product="tablet" msgid="1788868809638682503">"ಟ್ಯಾಬ್ಲೆಟ್ನ ಸಮಯ ವಲಯವನ್ನು ಬದಲಾಯಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permdesc_setTimeZone" product="tv" msgid="9069045914174455938">"ನಿಮ್ಮ Android TV ಸಾಧನದ ಸಮಯವಲಯವನ್ನು ಬದಲಾಯಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permdesc_setTimeZone" product="default" msgid="4611828585759488256">"ಫೋನ್ನ ಸಮಯ ವಲಯವನ್ನು ಬದಲಾಯಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
@@ -837,7 +837,7 @@
<string name="permdesc_updatePackagesWithoutUserAction" msgid="4567739631260526366">"ಬಳಕೆದಾರರ ಕ್ರಿಯೆಯಿಲ್ಲದೆ ಈ ಹಿಂದೆ ಇನ್ಸ್ಟಾಲ್ ಮಾಡಿದ ಆ್ಯಪ್ ಅನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಲು ಹೋಲ್ಡರ್ ಅನ್ನು ಅನುಮತಿಸುತ್ತದೆ"</string>
<string name="permlab_writeVerificationStateE2eeContactKeys" msgid="3990742344778360457">"ಇತರ ಆ್ಯಪ್ಗಳ ಮಾಲೀಕತ್ವದ E2EE ಸಂಪರ್ಕ ಕೀಗಳ ಪರಿಶೀಲನೆಯ ಸ್ಥಿತಿಗಳನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಿ"</string>
<string name="permdesc_writeVerificationStateE2eeContactKeys" msgid="8453156829747427041">"ಇತರ ಆ್ಯಪ್ಗಳ ಮಾಲೀಕತ್ವದ E2EE ಸಂಪರ್ಕ ಕೀಗಳ ಪರಿಶೀಲನೆಯ ಸ್ಥಿತಿಗಳನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ"</string>
- <string name="policylab_limitPassword" msgid="4851829918814422199">"ಪಾಸ್ವರ್ಡ್ ನಿಮಯಗಳನ್ನು ಹೊಂದಿಸಿ"</string>
+ <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>
@@ -863,11 +863,11 @@
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"ಎಚ್ಚರಿಕೆ ಇಲ್ಲದೆ ಈ Android TV ಸಾಧನದಲ್ಲಿನ ಈ ಬಳಕೆದಾರರ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕುತ್ತದೆ."</string>
<string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"ಎಚ್ಚರಿಕೆಯಿಲ್ಲದೆ ಈ ಇನ್ಫೋಟೈನ್ಮೆಂಟ್ ಸಿಸ್ಟಂನಲ್ಲಿ ಈ ಪ್ರೊಫೈಲ್ನ ಡೇಟಾವನ್ನು ಅಳಿಸಿ."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"ಯಾವುದೇ ಸೂಚನೆ ಇಲ್ಲದೆ ಈ ಫೋನ್ನಲ್ಲಿ ಈ ಬಳಕೆದಾರರ ಡೇಟಾವನ್ನು ಅಳಿಸಿ."</string>
- <string name="policylab_setGlobalProxy" msgid="215332221188670221">"ಸಾಧನವನ್ನು ಜಾಗತಿಕ ಪ್ರಾಕ್ಸಿಗೆ ಹೊಂದಿಸಿ"</string>
- <string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ನೀತಿಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿದಾಗ ಬಳಸಬೇಕಾದ ಸಾಧನದ ಜಾಗತಿಕ ಪ್ರಾಕ್ಸಿಯನ್ನು ಹೊಂದಿಸಿ. ಸಾಧನದ ಮಾಲೀಕರು ಮಾತ್ರ ಜಾಗತಿಕ ಪ್ರಾಕ್ಸಿಯನ್ನು ಹೊಂದಿಸಬಹುದಾಗಿರುತ್ತದೆ."</string>
- <string name="policylab_expirePassword" msgid="6015404400532459169">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಪಾಸ್ವರ್ಡ್ ಮುಕ್ತಾಯವನ್ನು ಹೊಂದಿಸಿ"</string>
+ <string name="policylab_setGlobalProxy" msgid="215332221188670221">"ಸಾಧನವನ್ನು ಜಾಗತಿಕ ಪ್ರಾಕ್ಸಿಗೆ ಸೆಟ್ ಮಾಡಿ"</string>
+ <string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ನೀತಿಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿದಾಗ ಬಳಸಬೇಕಾದ ಸಾಧನದ ಜಾಗತಿಕ ಪ್ರಾಕ್ಸಿಯನ್ನು ಸೆಟ್ ಮಾಡಿ. ಸಾಧನದ ಮಾಲೀಕರು ಮಾತ್ರ ಜಾಗತಿಕ ಪ್ರಾಕ್ಸಿಯನ್ನು ಹೊಂದಿಸಬಹುದಾಗಿರುತ್ತದೆ."</string>
+ <string name="policylab_expirePassword" msgid="6015404400532459169">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಪಾಸ್ವರ್ಡ್ ಮುಕ್ತಾಯವನ್ನು ಸೆಟ್ ಮಾಡಿ"</string>
<string name="policydesc_expirePassword" msgid="9136524319325960675">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಪಾಸ್ವರ್ಡ್, ಪಿನ್, ಅಥವಾ ನಮೂನೆಯನ್ನು ಹೆಚ್ಚು ಪದೆ ಪದೇ ಬದಲಾಯಿಸಬೇಕಾಗಿರುತ್ತದೆ ಎಂಬುದನ್ನು ಬದಲಾಯಿಸಿ."</string>
- <string name="policylab_encryptedStorage" msgid="9012936958126670110">"ಸಂಗ್ರಹಣೆ ಎನ್ಕ್ರಿಪ್ಶನ್ ಹೊಂದಿಸಿ"</string>
+ <string name="policylab_encryptedStorage" msgid="9012936958126670110">"ಸಂಗ್ರಹಣೆ ಎನ್ಕ್ರಿಪ್ಶನ್ ಸೆಟ್ ಮಾಡಿ"</string>
<string name="policydesc_encryptedStorage" msgid="1102516950740375617">"ಸಂಗ್ರಹಿಸಿರುವ ಆ್ಯಪ್ ಡೇಟಾವನ್ನು ಎನ್ಕ್ರಿಪ್ಟ್ ಮಾಡಬೇಕಾದ ಅಗತ್ಯವಿದೆ."</string>
<string name="policylab_disableCamera" msgid="5749486347810162018">"ಕ್ಯಾಮರಾಗಳನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
<string name="policydesc_disableCamera" msgid="3204405908799676104">"ಎಲ್ಲಾ ಸಾಧನ ಕ್ಯಾಮರಾಗಳ ಬಳಕೆಯನ್ನು ತಡೆಯಿರಿ."</string>
@@ -1103,7 +1103,7 @@
<string name="js_dialog_before_unload_negative_button" msgid="3873765747622415310">"ಈ ಪುಟದಲ್ಲಿಯೇ ಇರಿ"</string>
<string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nನೀವು ಈ ಪುಟದಿಂದಾಚೆಗೆ ನ್ಯಾವಿಗೇಟ್ ಮಾಡಲು ಖಚಿತವಾಗಿ ಬಯಸುವಿರಾ?"</string>
<string name="autofill_window_title" msgid="4379134104008111961">"<xliff:g id="SERVICENAME">%1$s</xliff:g> ಸಹಾಯದಿಂದ ಸ್ವಯಂ-ಭರ್ತಿ"</string>
- <string name="permlab_setAlarm" msgid="1158001610254173567">"ಅಲಾರಮ್ ಹೊಂದಿಸಿ"</string>
+ <string name="permlab_setAlarm" msgid="1158001610254173567">"ಅಲಾರಮ್ ಸೆಟ್ ಮಾಡಿ"</string>
<string name="permdesc_setAlarm" msgid="2185033720060109640">"ಸ್ಥಾಪಿಸಲಾದ ಅಲಾರಮ್ ಗಡಿಯಾರ ಅಪ್ಲಿಕೇಶನ್ನಲ್ಲಿ ಅಲಾರಮ್ ಹೊಂದಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಕೆಲವು ಅಲಾರಮ್ ಗಡಿಯಾರ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಈ ವೈಶಿಷ್ಟ್ಯವನ್ನು ಕಾರ್ಯಗತಗೊಳಿಸದಿರಬಹುದು."</string>
<string name="permlab_addVoicemail" msgid="4770245808840814471">"ಧ್ವನಿಮೇಲ್ ಸೇರಿಸಿ"</string>
<string name="permdesc_addVoicemail" msgid="5470312139820074324">"ನಿಮ್ಮ ದ್ವನಿಮೇಲ್ ಇನ್ಬಾಕ್ಸ್ಗೆ ಸಂದೇಶಗಳನ್ನು ಸೇರಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
@@ -1158,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g>ಗಂ ಯಲ್ಲಿ"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>ದಿ ದಲ್ಲಿ"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g>ವ ದಲ್ಲಿ"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>ನಿಮಿಷದ ಹಿಂದೆ"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>ಗಂಟೆ ಹಿಂದೆ"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>ದಿನಗಳ ಹಿಂದೆ"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>ವರ್ಷದ ಹಿಂದೆ"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>ನಿಮಿಷ"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>ಗಂಟೆ"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>ದಿನ"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>ವರ್ಷ"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g>ನಿಮಿಷದಲ್ಲಿ"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g>ಗಂಟೆಯಲ್ಲಿ"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g>ದಿನದಲ್ಲಿ"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g>ವರ್ಷದಲ್ಲಿ"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>ನಿಮಿಷದ ಹಿಂದೆ"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>ಗಂಟೆ ಹಿಂದೆ"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>ದಿನಗಳ ಹಿಂದೆ"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>ವರ್ಷದ ಹಿಂದೆ"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# ನಿಮಿಷದ ಹಿಂದೆ}one{# ನಿಮಿಷಗಳ ಹಿಂದೆ}other{# ನಿಮಿಷಗಳ ಹಿಂದೆ}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ಗಂಟೆಯ ಹಿಂದೆ}one{# ಗಂಟೆಗಳ ಹಿಂದೆ}other{# ಗಂಟೆಗಳ ಹಿಂದೆ}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ದಿನದ ಹಿಂದೆ}one{# ದಿನಗಳ ಹಿಂದೆ}other{# ದಿನಗಳ ಹಿಂದೆ}}"</string>
@@ -1389,7 +1405,7 @@
<string name="install_carrier_app_notification_button" msgid="6257740533102594290">"ಆ್ಯಪ್ ಡೌನ್ಲೋಡ್ ಮಾಡಿ"</string>
<string name="carrier_app_notification_title" msgid="5815477368072060250">"ಹೊಸ ಸಿಮ್ ಸೇರಿಸಲಾಗಿದೆ"</string>
<string name="carrier_app_notification_text" msgid="6567057546341958637">"ಇದನ್ನು ಸ್ಥಾಪಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
- <string name="time_picker_dialog_title" msgid="9053376764985220821">"ಸಮಯವನ್ನು ಹೊಂದಿಸಿ"</string>
+ <string name="time_picker_dialog_title" msgid="9053376764985220821">"ಸಮಯವನ್ನು ಸೆಟ್ ಮಾಡಿ"</string>
<string name="date_picker_dialog_title" msgid="5030520449243071926">"ದಿನಾಂಕವನ್ನು ಸೆಟ್ ಮಾಡಿ"</string>
<string name="date_time_set" msgid="4603445265164486816">"ಹೊಂದಿಸು"</string>
<string name="date_time_done" msgid="8363155889402873463">"ಆಯಿತು"</string>
@@ -1590,7 +1606,7 @@
<string name="time_picker_increment_hour_button" msgid="3063572723197178242">"ಗಂಟೆಯನ್ನು ಹೆಚ್ಚಿಸಿ"</string>
<string name="time_picker_decrement_hour_button" msgid="584101766855054412">"ಗಂಟೆಯನ್ನು ಕಡಿಮೆ ಮಾಡಿ"</string>
<string name="time_picker_increment_set_pm_button" msgid="5889149366900376419">"PM ಹೊಂದಿಸು"</string>
- <string name="time_picker_decrement_set_am_button" msgid="1422608001541064087">"AM ಹೊಂದಿಸಿ"</string>
+ <string name="time_picker_decrement_set_am_button" msgid="1422608001541064087">"AM ಸೆಟ್ ಮಾಡಿ"</string>
<string name="date_picker_increment_month_button" msgid="3447263316096060309">"ತಿಂಗಳನ್ನು ಹೆಚ್ಚಿಸಿ"</string>
<string name="date_picker_decrement_month_button" msgid="6531888937036883014">"ತಿಂಗಳು ಕಡಿಮೆಮಾಡಿ"</string>
<string name="date_picker_increment_day_button" msgid="4349336637188534259">"ದಿನವನ್ನು ಹೆಚ್ಚಿಸಿ"</string>
@@ -2076,7 +2092,7 @@
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB ಡೀಬಗ್ ಮಾಡುವಿಕೆ"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"ಗಂಟೆ"</string>
<string name="time_picker_minute_label" msgid="8307452311269824553">"ನಿಮಿಷ"</string>
- <string name="time_picker_header_text" msgid="9073802285051516688">"ಸಮಯವನ್ನು ಹೊಂದಿಸಿ"</string>
+ <string name="time_picker_header_text" msgid="9073802285051516688">"ಸಮಯವನ್ನು ಸೆಟ್ ಮಾಡಿ"</string>
<string name="time_picker_input_error" msgid="8386271930742451034">"ಮಾನ್ಯವಾದ ಸಮಯವನ್ನು ನಮೂದಿಸಿ"</string>
<string name="time_picker_prompt_label" msgid="303588544656363889">"ಸಮಯ ಟೈಪ್ ಮಾಡಿ"</string>
<string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"ಸಮಯವನ್ನು ನಮೂದಿಸಲು ಪಠ್ಯದ ನಮೂನೆಗೆ ಬದಲಿಸಿ."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index b8bbacb..5a476836 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"발신자 번호가 기본적으로 제한됨으로 설정됩니다. 다음 통화: 제한되지 않음"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"발신자 번호가 기본적으로 제한되지 않음으로 설정됩니다. 다음 통화: 제한됨"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"발신자 번호가 기본적으로 제한되지 않음으로 설정됩니다. 다음 통화: 제한되지 않음"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"이 앱은 16KB와 호환되지 않습니다. APK 정렬 검사에 실패했습니다. 이 앱은 페이지 크기 호환 모드를 사용하여 실행됩니다. 최상의 호환성을 위해 16KB를 지원하도록 애플리케이션을 다시 컴파일하세요. 자세한 내용은 <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>를 참고하세요."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"이 앱은 16KB와 호환되지 않습니다. ELF 정렬 검사에 실패했습니다. 이 앱은 페이지 크기 호환 모드를 사용하여 실행됩니다. 최상의 호환성을 위해 16KB를 지원하도록 애플리케이션을 다시 컴파일하세요. 자세한 내용은 <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>를 참고하세요."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"이 앱은 16KB와 호환되지 않습니다. APK 및 ELF 정렬 검사에 실패했습니다. 이 앱은 페이지 크기 호환 모드를 사용하여 실행됩니다. 최상의 호환성을 위해 16KB를 지원하도록 애플리케이션을 다시 컴파일하세요. 자세한 내용은 <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>를 참고하세요."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"서비스가 준비되지 않았습니다."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"발신자 번호 설정을 변경할 수 없습니다."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g> 이동통신사로 데이터가 변경됨"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g>시간 후"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>일 후"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g>년 후"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{#분 전}other{#분 전}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{#시간 전}other{#시간 전}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{#일 전}other{#일 전}}"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 70c4d8e..2ca59918 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1158,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> с. кийин"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> к. кийин"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> ж. кийин"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# мүнөт мурун}other{# мүнөт мурун}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# саат мурун}other{# саат мурун}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# күн мурун}other{# күн мурун}}"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 837cfc3..3b234e8 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1158,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"ໃນ <xliff:g id="COUNT">%d</xliff:g>ຊມ"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"ໃນ <xliff:g id="COUNT">%d</xliff:g>ມ"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"ໃນ <xliff:g id="COUNT">%d</xliff:g>ປ"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# ນາທີກ່ອນ}other{# ນາທີກ່ອນ}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ຊົ່ວໂມງກ່ອນ}other{# ຊົ່ວໂມງກ່ອນ}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ມື້ກ່ອນ}other{# ມື້ກ່ອນ}}"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index a281355..c1eaa04 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1160,6 +1160,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"po <xliff:g id="COUNT">%d</xliff:g> val."</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"po <xliff:g id="COUNT">%d</xliff:g> d."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"po <xliff:g id="COUNT">%d</xliff:g> m."</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Prieš # minutę}one{Prieš # minutę}few{Prieš # minutes}many{Prieš # minutės}other{Prieš # minučių}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Prieš # valandą}one{Prieš # valandą}few{Prieš # valandas}many{Prieš # valandos}other{Prieš # valandų}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Prieš # dieną}one{Prieš # dieną}few{Prieš # dienas}many{Prieš # dienos}other{Prieš # dienų}}"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index a1c134b..03c7c3c 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Zvanītāja ID noklusējumi ir iestatīti uz Ierobežots. Nākamais zvans: nav ierobežots"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Zvanītāja ID noklusējumi ir iestatīti uz Nav ierobežots. Nākamais zvans: ierobežots"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Zvanītāja ID noklusējumi ir iestatīti uz Nav ierobežots. Nākamais zvans: nav ierobežots"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Šī lietotne nav saderīga ar 16 KB lapām. APK saskaņošanas pārbaude neizdevās. Šī lietotne tiks palaista, izmantojot ar lapas izmēru saderīgu režīmu. Lai uzlabotu saderību, lūdzu, atkārtoti kompilējiet lietojumprogrammu, nodrošinot atbalstu 16 KB lapām. Plašāku informāciju skatiet vietnē <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Šī lietotne nav saderīga ar 16 KB lapām. ELF saskaņošanas pārbaude neizdevās. Šī lietotne tiks palaista, izmantojot ar lapas izmēru saderīgu režīmu. Lai uzlabotu saderību, lūdzu, atkārtoti kompilējiet lietojumprogrammu, nodrošinot atbalstu 16 KB lapām. Plašāku informāciju skatiet vietnē <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Šī lietotne nav saderīga ar 16 KB lapām. APK un ELF saskaņošanas pārbaudes neizdevās. Šī lietotne tiks palaista, izmantojot ar lapas izmēru saderīgu režīmu. Lai uzlabotu saderību, lūdzu, atkārtoti kompilējiet lietojumprogrammu, nodrošinot atbalstu 16 KB lapām. Plašāku informāciju skatiet vietnē <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Pakalpojums netiek nodrošināts."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Zvanītāja ID iestatījumu nevar mainīt."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Tiek izmantots operatora <xliff:g id="CARRIERDISPLAY">%s</xliff:g> datu savienojums"</string>
@@ -1162,6 +1159,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"pēc <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"pēc <xliff:g id="COUNT">%d</xliff:g> d."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"pēc <xliff:g id="COUNT">%d</xliff:g> g."</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Pirms vienas minūtes}zero{Pirms # minūtēm}one{Pirms vairākām minūtēm, minūšu skaits: #}other{Pirms vairākām minūtēm, minūšu skaits: #}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Pirms vienas stundas}zero{Pirms # stundām}one{Pirms vairākām stundām, stundu skaits: #}other{Pirms vairākām stundām, stundu skaits: #}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Pirms vienas dienas}zero{Pirms # dienām}one{Pirms vairākām dienām, dienu skaits: #}other{Pirms vairākām dienām, dienu skaits: #}}"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 74e5bf0..e299c8b 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Стандардно, ID на повикувач е скриен. Следен повик: не е скриен"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Стандардно, ID на повикувач не е скриен. Следен повик: скриен"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Стандардно, ID на повикувач не е скриен. Следен повик: не е скриен"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Апликацијава не е компатибилна со 16 KB. Проверката за усогласување на АПК не успеа. Апликацијава ќе се извршува со режим компатибилен со големината на страницата. За најдобра компатибилност, рекомпилирајте ја апликацијата со поддршка за 16 KB. За повеќе информации, одете на <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Апликацијава не е компатибилна со 16 KB. Проверката за усогласување на ELF не успеа. Апликацијава ќе се извршува со режим компатибилен со големината на страницата. За најдобра компатибилност, рекомпилирајте ја апликацијата со поддршка за 16 KB. За повеќе информации, одете на <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Апликацијава не е компатибилна со 16 KB. Проверките за усогласување на АПК и ELF се неуспешни. Апликацијава ќе се извршува со режим компатибилен со големината на страницата. За најдобра компатибилност, рекомпилирајте ја апликацијата со поддршка за 16 KB. За повеќе информации, одете на <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Услугата не е предвидена."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Не може да го промените поставувањето за ID на повикувач."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Мобилниот интернет се префрли на <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"по <xliff:g id="COUNT">%d</xliff:g> ч."</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"по <xliff:g id="COUNT">%d</xliff:g> д."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"по <xliff:g id="COUNT">%d</xliff:g> г."</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Пред # минута}one{Пред # минута}other{Пред # минути}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Пред # час}one{Пред # час}other{Пред # часа}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Пред # ден}one{Пред # ден}other{Пред # дена}}"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 37f51c5..3c08c01 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"നിയന്ത്രിക്കേണ്ട സ്ഥിര കോളർ ഐഡികൾ. അടുത്ത കോൾ: നിയന്ത്രിച്ചിട്ടില്ല"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"നിയന്ത്രിക്കേണ്ടതല്ലാത്ത സ്ഥിര കോളർ ഐഡികൾ. അടുത്ത കോൾ: നിയന്ത്രിച്ചിട്ടുണ്ട്"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"നിയന്ത്രിക്കേണ്ടതല്ലാത്ത സ്ഥിര കോളർ ഐഡികൾ. അടുത്ത കോൾ: നിയന്ത്രിച്ചിട്ടില്ല"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"ഈ ആപ്പ് 16 KB-ക്ക് അനുയോജ്യമല്ല. APK അലൈൻമെന്റ് പരിശോധന നടത്താനായില്ല. പേജ് വലുപ്പത്തിന് അനുയോജ്യമായ മോഡ് ഉപയോഗിച്ച് ഈ ആപ്പ് റൺ ചെയ്യും. മികച്ച അനുയോജ്യതയ്ക്കായി, 16 KB പിന്തുണയോടെ ആപ്പ് വീണ്ടും കംപൈൽ ചെയ്യുക. കൂടുതൽ വിവരങ്ങൾക്ക്, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> കാണുക"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"ഈ ആപ്പ് 16 KB-ക്ക് അനുയോജ്യമല്ല. ELF അലൈൻമെന്റ് പരിശോധന നടത്താനായില്ല. പേജ് വലുപ്പത്തിന് അനുയോജ്യമായ മോഡ് ഉപയോഗിച്ച് ഈ ആപ്പ് റൺ ചെയ്യും. മികച്ച അനുയോജ്യതയ്ക്കായി, 16 KB പിന്തുണയോടെ ആപ്പ് വീണ്ടും കംപൈൽ ചെയ്യുക. കൂടുതൽ വിവരങ്ങൾക്ക്, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> കാണുക"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"ഈ ആപ്പ് 16 KB-ക്ക് അനുയോജ്യമല്ല. APK, ELF അലൈൻമെന്റ് പരിശോധനകൾ നടത്താനായില്ല. പേജ് വലുപ്പത്തിന് അനുയോജ്യമായ മോഡ് ഉപയോഗിച്ച് ഈ ആപ്പ് റൺ ചെയ്യും. മികച്ച അനുയോജ്യതയ്ക്കായി, 16 KB പിന്തുണയോടെ ആപ്പ് വീണ്ടും കംപൈൽ ചെയ്യുക. കൂടുതൽ വിവരങ്ങൾക്ക്, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> കാണുക"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"സേവനം വ്യവസ്ഥ ചെയ്തിട്ടില്ല."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"വിളിച്ച നമ്പർ ക്രമീകരണം നിങ്ങൾക്ക് മാറ്റാനാവില്ല."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g> എന്നതിലേക്ക് ഡാറ്റ മാറ്റി"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g>മണിക്കൂറിൽ"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>ദിവസത്തിൽ"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g>വർഷത്തിനുള്ളിൽ"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# മിനിറ്റ് മുമ്പ്}other{# മിനിറ്റ് മുമ്പ്}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# മണിക്കൂർ മുമ്പ്}other{# മണിക്കൂർ മുമ്പ്}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ദിവസം മുമ്പ്}other{# ദിവസം മുമ്പ്}}"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index fce948e..e32eff0 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Дуудлага хийгчийн ID хязгаарлагдсан. Дараагийн дуудлага: Хязгаарлагдаагүй"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Дуудлага хийгчийн ID хязгаарлагдаагүй. Дараагийн дуудлага: Хязгаарлагдсан"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Дуудлага хийгчийн ID хязгаарлагдсан. Дараагийн дуудлага: Хязгаарлагдсан"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Энэ апп 16 КБ-ын хэмжээтэй хуудастай тохиромжгүй байна. APK эгнүүлэлтийн шалгалт амжилтгүй боллоо. Энэ апп хуудасны хэмжээтэй тохирох горимыг ашиглан ажиллана. Хамгийн тохиромжтой байлгахын тулд 16 КБ-ын дэмжлэгээр аппликэйшнийг дахин хөрвүүлнэ үү. Нэмэлт мэдээлэл авах бол <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> холбоосыг харна уу"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Энэ апп 16 КБ-ын хэмжээтэй хуудастай тохиромжгүй байна. ELF эгнүүлэлтийн шалгалт амжилтгүй боллоо. Энэ апп хуудасны хэмжээтэй тохирох горимыг ашиглан ажиллана. Хамгийн тохиромжтой байлгахын тулд 16 КБ-ын дэмжлэгээр аппликэйшнийг дахин хөрвүүлнэ үү. Нэмэлт мэдээлэл авах бол <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> холбоосыг харна уу"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Энэ апп 16 КБ-ын хэмжээтэй хуудастай тохиромжгүй байна. APK, ELF эгнүүлэлтийн шалгалт амжилтгүй боллоо. Энэ апп хуудасны хэмжээтэй тохирох горимыг ашиглан ажиллана. Хамгийн тохиромжтой байлгахын тулд 16 КБ-ын дэмжлэгээр аппликэйшнийг дахин хөрвүүлнэ үү. Нэмэлт мэдээлэл авах бол <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> холбоосыг харна уу"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Үйлчилгээ провишн хийгдээгүй ."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Та дуудлага хийгчийн ID тохиргоог солиж чадахгүй."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Өгөгдлийг <xliff:g id="CARRIERDISPLAY">%s</xliff:g> руу шилжүүлсэн"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g>цагийн дараа"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>хоногийн дараа"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g>жилийн дараа"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# минутын өмнө}other{# минутын өмнө}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# цагийн өмнө}other{# цагийн өмнө}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# хоногийн өмнө}other{# хоногийн өмнө}}"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index cf76311..abe9a94 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1158,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> तासांमध्ये"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> दिवसांमध्ये"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> वर्षांमध्ये"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# मिनिटापूर्वी}other{# मिनिटांपूर्वी}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# तासापूर्वी}other{# तासांपूर्वी}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# दिवसापूर्वी}other{# दिवसांपूर्वी}}"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 34d7654..21925f8 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ID pemanggil secara lalainya ditetapkan kepada terhad. Panggilan seterusnya: Tidak terhad"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ID pemanggil secara lalainya ditetapkan kepada tidak terhad. Panggilan seterusnya: Terhad"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID pemanggil secara lalainya ditetapkan kepada tidak dihadkan. Panggilan seterusnya: Tidak terhad"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Apl ini tidak serasi untuk 16 KB. Semakan penjajaran APK gagal. Apl ini akan dijalankan menggunakan mod serasi saiz halaman. Untuk keserasian yang terbaik, sila susun semula aplikasi dengan sokongan 16 KB. Untuk mendapatkan maklumat lanjut, lihat <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Apl ini tidak serasi untuk 16 KB. Semakan penjajaran ELF gagal. Apl ini akan dijalankan menggunakan mod serasi saiz halaman. Untuk keserasian yang terbaik, sila susun semula aplikasi dengan sokongan 16 KB. Untuk mendapatkan maklumat lanjut, lihat <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Apl ini tidak serasi untuk 16 KB. Semakan penjajaran APK dan ELF gagal. Apl ini akan dijalankan menggunakan mod serasi saiz halaman. Untuk keserasian yang terbaik, sila susun semula aplikasi dengan sokongan 16 KB. Untuk mendapatkan maklumat lanjut, lihat <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Perkhidmatan yang tidak diuntukkan."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Anda tidak boleh mengubah tetapan ID pemanggil."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Data ditukar kepada <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"dalam <xliff:g id="COUNT">%d</xliff:g>j"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"dalam <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"dalam <xliff:g id="COUNT">%d</xliff:g>t"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g>m yang lalu"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g>j yang lalu"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g>h yang lalu"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g>t yang lalu"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>jam"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g>h"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>thn"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"selepas <xliff:g id="COUNT">%d</xliff:g>minit"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"selepas <xliff:g id="COUNT">%d</xliff:g>jam"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"dalam <xliff:g id="COUNT">%d</xliff:g>h"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"selepas <xliff:g id="COUNT">%d</xliff:g>thn"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g>minit yang lalu"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g>jam yang lalu"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g>h yang lalu"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g>thn yang lalu"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minit yang lalu}other{# minit yang lalu}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# jam yang lalu}other{# jam yang lalu}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# hari yang lalu}other{# hari yang lalu}}"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 45be6f5..ec6eff4 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ပုံသေအားဖြင့် ခေါ်ဆိုသူအိုင်ဒီ(Caller ID)အား ကန့်သတ်ထားသည်။ နောက်ထပ်အဝင်ခေါ်ဆိုမှု-ကန့်သတ်မထားပါ။"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ပုံသေအားဖြင့် ခေါ်ဆိုသူအိုင်ဒီ(Caller ID)အား ကန့်သတ်မထားပါ။ နောက်ထပ်အဝင်ခေါ်ဆိုမှု-ကန့်သတ်ထားသည်။"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ပုံသေအားဖြင့် ခေါ်ဆိုသူအိုင်ဒီ(Caller ID)အား ကန့်သတ်မထားပါ။ နောက်ထပ်အဝင်ခေါ်ဆိုမှု-ကန့်သတ်မထားပါ။"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"ဤအက်ပ်သည် ၁၆ KB နှင့် တွဲမသုံးနိုင်ပါ။ APK ချိန်ညှိခြင်း စစ်ဆေးမှု မအောင်မြင်ပါ။ ဤအက်ပ်သည် တွဲသုံးနိုင်သော စာမျက်နှာအရွယ်အစားမုဒ်သုံး၍ လုပ်ဆောင်ပါမည်။ အကောင်းဆုံး တွဲသုံးနိုင်မှုအတွက် အပလီကေးရှင်းကို ၁၆ KB ပံ့ပိုးမှုဖြင့် ပြန်လည်တည်ဆောက်ပါ။ နောက်ထပ်အချက်အလက်အတွက် <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> တွင် ကြည့်ပါ"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"ဤအက်ပ်သည် ၁၆ KB နှင့် တွဲမသုံးနိုင်ပါ။ ELF ချိန်ညှိခြင်း စစ်ဆေးမှု မအောင်မြင်ပါ။ ဤအက်ပ်သည် တွဲသုံးနိုင်သော စာမျက်နှာအရွယ်အစားမုဒ်သုံး၍ လုပ်ဆောင်ပါမည်။ အကောင်းဆုံး တွဲသုံးနိုင်မှုအတွက် အပလီကေးရှင်းကို ၁၆ KB ပံ့ပိုးမှုဖြင့် ပြန်လည်တည်ဆောက်ပါ။ နောက်ထပ်အချက်အလက်အတွက် <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> တွင် ကြည့်ပါ"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"ဤအက်ပ်သည် ၁၆ KB နှင့် တွဲမသုံးနိုင်ပါ။ APK နှင့် ELF ချိန်ညှိခြင်း စစ်ဆေးမှု မအောင်မြင်ပါ။ ဤအက်ပ်သည် တွဲသုံးနိုင်သော စာမျက်နှာအရွယ်အစားမုဒ်သုံး၍ လုပ်ဆောင်ပါမည်။ အကောင်းဆုံး တွဲသုံးနိုင်မှုအတွက် အပလီကေးရှင်းကို ၁၆ KB ပံ့ပိုးမှုဖြင့် ပြန်လည်တည်ဆောက်ပါ။ နောက်ထပ်အချက်အလက်အတွက် <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> တွင် ကြည့်ပါ"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"ဝန်ဆောင်မှုအား ကန့်သတ်မထားပါ"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"သင်သည် ခေါ်ဆိုသူ ID ဆက်တင်ကို မပြောင်းလဲနိုင်ပါ။"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"ဒေတာကို <xliff:g id="CARRIERDISPLAY">%s</xliff:g> သို့ ပြောင်းထားသည်"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> နာရီအတွင်း"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> ရက်အတွင်း"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> နှစ်အတွင်း"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{ပြီးခဲ့သော # မိနစ်}other{ပြီးခဲ့သော # မိနစ်}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{ပြီးခဲ့သော # နာရီ}other{ပြီးခဲ့သော # နာရီ}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{ပြီးခဲ့သော # ရက်}other{ပြီးခဲ့သော # ရက်}}"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 910d70d..fd80ab4 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Nummervisning er begrenset som standard. Neste anrop: Ikke begrenset"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Nummervisning er ikke begrenset som standard. Neste anrop: Begrenset"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Nummervisning er ikke begrenset som standard. Neste anrop: Ikke begrenset"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Denne appen er ikke kompatibel med 16 kB. Kontrollen av APK-justering mislyktes. Denne appen kjøres i modus for sideformatkompatibilitet. For å få best mulig kompatibilitet bør du kompilere appen på nytt med støtte for 16 kB. Du finner mer informasjon på <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Denne appen er ikke kompatibel med 16 kB. Kontrollen av ELF-justering mislyktes. Denne appen kjøres i modus for sideformatkompatibilitet. For å få best mulig kompatibilitet bør du kompilere appen på nytt med støtte for 16 kB. Du finner mer informasjon på <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Denne appen er ikke kompatibel med 16 kB. APK- og ELF-justeringskontrollene mislyktes. Denne appen kjøres i modus for sideformatkompatibilitet. For å få best mulig kompatibilitet bør du kompilere appen på nytt med støtte for 16 kB. Du finner mer informasjon på <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"SIM-kortet er ikke tilrettelagt for tjenesten."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Du kan ikke endre innstillingen for anrops-ID."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Byttet data til <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"om <xliff:g id="COUNT">%d</xliff:g> t"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"om <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"om <xliff:g id="COUNT">%d</xliff:g> år"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{for # minutt siden}other{For # minutter siden}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# time siden}other{# timer siden}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{For # dag siden}other{For # dager siden}}"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 5e6d850..6fabb06 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"कलर ID पूर्वनिर्धारितको लागि रोकावट छ। अर्को कल: रोकावट छैन"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"कलर ID पूर्वनिर्धारितदेखि प्रतिबन्धित छैन। अर्को कल: प्रतिबन्धित छ"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"कलर ID पूर्वनिर्धारितको लागि रोकावट छैन। अर्को कल: रोकावट छैन"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"यो एप १६ के.बि. को पेजसँग कम्प्याटिबल छैन। APK को एलाइनमेन्ट जाँच्न सकिएन। यो एप पेजको साइजसँग कम्प्याटिबल मोड प्रयोग गरेर चलाइने छ। उत्कृष्ट कम्प्याटिबिलिटीका लागि कृपया यो एप १६ के.बि. को पेज प्रयोग गर्न मिल्ने गरी रिकम्पलाइल गर्नुहोस्। यस सम्बन्धमा थप जानकारी प्राप्त गर्न <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> हेर्नुहोस्"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"यो एप १६ के.बि. को पेजसँग कम्प्याटिबल छैन। ELF को एलाइनमेन्ट जाँच्न सकिएन। यो एप पेजको साइजसँग कम्प्याटिबल मोड प्रयोग गरेर चलाइने छ। उत्कृष्ट कम्प्याटिबिलिटीका लागि कृपया यो एप १६ के.बि. को पेज प्रयोग गर्न मिल्ने गरी रिकम्पलाइल गर्नुहोस्। यस सम्बन्धमा थप जानकारी प्राप्त गर्न <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> हेर्नुहोस्"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"यो एप १६ के.बि. को पेजसँग कम्प्याटिबल छैन। APK र ELF को एलाइनमेन्ट जाँच्न सकिएन। यो एप पेजको साइजसँग कम्प्याटिबल मोड प्रयोग गरेर चलाइने छ। उत्कृष्ट कम्प्याटिबिलिटीका लागि कृपया यो एप १६ के.बि. को पेज प्रयोग गर्न मिल्ने गरी रिकम्पलाइल गर्नुहोस्। यस सम्बन्धमा थप जानकारी प्राप्त गर्न <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> हेर्नुहोस्"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"सेवाको व्यवस्था छैन।"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"तपाईं कलर ID सेटिङ परिवर्तन गर्न सक्नुहुन्न।"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g> को डेटा प्रयोग गर्न थालिएको छ"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> घण्टाभित्र"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> दिनभित्र"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> वर्षभित्र"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# मिनेटअघि}other{# मिनेटअघि}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# घण्टाअघि}other{# घण्टाअघि}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# दिनअघि}other{# दिनअघि}}"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index b45f6cd..b93b916 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Beller-ID standaard ingesteld op \'beperkt\'. Volgend gesprek: onbeperkt."</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Beller-ID standaard ingesteld op \'onbeperkt\'. Volgend gesprek: beperkt."</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Beller-ID standaard ingesteld op \'onbeperkt\'. Volgend gesprek: onbeperkt."</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Deze app is niet compatibel met 16 KB. APK-uitlijningscontrole mislukt. Deze app wordt uitgevoerd in de compatibele modus voor paginagrootte. Voor de beste compatibiliteit moet je de app opnieuw compileren met ondersteuning voor 16 KB. Zie <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> voor meer informatie."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Deze app is niet compatibel met 16 KB. ELF-uitlijningscontrole mislukt. Deze app wordt uitgevoerd in de compatibele modus voor paginagrootte. Voor de beste compatibiliteit moet je de app opnieuw compileren met ondersteuning voor 16 KB. Zie <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> voor meer informatie."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Deze app is niet compatibel met 16 KB. APK- en ELF-uitlijningscontroles mislukt. Deze app wordt uitgevoerd in de compatibele modus voor paginagrootte. Voor de beste compatibiliteit moet je de app opnieuw compileren met ondersteuning voor 16 KB. Zie <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> voor meer informatie."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Service niet voorzien."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"U kunt de instelling voor de beller-ID niet wijzigen."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Mobiele data overgeschakeld naar <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"over <xliff:g id="COUNT">%d</xliff:g> u"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"over <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"over <xliff:g id="COUNT">%d</xliff:g> j"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuut geleden}other{# minuten geleden}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# uur geleden}other{# uur geleden}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# dag geleden}other{# dagen geleden}}"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 9e57ca3..961822a8 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"କଲର୍ ଆଇଡି ଡିଫଲ୍ଟ ଭାବରେ ପ୍ରତିବନ୍ଧିତ। ପରବର୍ତ୍ତୀ କଲ୍: ପ୍ରତିବନ୍ଧିତ ନୁହେଁ"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"କଲର୍ ଆଇଡି ଡିଫଲ୍ଟ ଭାବରେ ପ୍ରତିବନ୍ଧିତ ନୁହେଁ। ପରବର୍ତ୍ତୀ କଲ୍: ପ୍ରତିବନ୍ଧିତ"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"କଲର୍ ଆଇଡି ଡିଫଲ୍ଟ ଭାବରେ ପ୍ରତିବନ୍ଧିତ ନୁହେଁ। ପରବର୍ତ୍ତୀ କଲ୍: ପ୍ରତିବନ୍ଧିତ ନୁହେଁ"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"ଏହି ଆପ 16 KB କମ୍ପାଟିବଲ ନୁହେଁ। APK ଆଲାଇନମେଣ୍ଟ ଯାଞ୍ଚ ବିଫଳ ହୋଇଛି। ପୃଷ୍ଠା ସାଇଜ କମ୍ପାଟିବଲ ମୋଡ ବ୍ୟବହାର କରି ଏହି ଆପକୁ ଚଲାଯିବ। ସର୍ବୋତ୍ତମ କମ୍ପାଟିବିଲିଟୀ ପାଇଁ ଦୟାକରି 16 KB ସପୋର୍ଟ ସହ ଆପ୍ଲିକେସନକୁ ପୁଣି କମ୍ପାଇଲ କରନ୍ତୁ। ଅଧିକ ସୂଚନା ପାଇଁ <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>କୁ ଦେଖନ୍ତୁ"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"ଏହି ଆପ 16 KB କମ୍ପାଟିବଲ ନୁହେଁ। ELF ଆଲାଇନମେଣ୍ଟ ଯାଞ୍ଚ ବିଫଳ ହୋଇଛି। ପୃଷ୍ଠା ସାଇଜ କମ୍ପାଟିବଲ ମୋଡ ବ୍ୟବହାର କରି ଏହି ଆପକୁ ଚଲାଯିବ। ସର୍ବୋତ୍ତମ କମ୍ପାଟିବିଲିଟୀ ପାଇଁ ଦୟାକରି 16 KB ସପୋର୍ଟ ସହ ଆପ୍ଲିକେସନକୁ ପୁଣି କମ୍ପାଇଲ କରନ୍ତୁ। ଅଧିକ ସୂଚନା ପାଇଁ <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>କୁ ଦେଖନ୍ତୁ"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"ଏହି ଆପ 16 KB କମ୍ପାଟିବଲ ନୁହେଁ। APK ଏବଂ ELF ଆଲାଇନମେଣ୍ଟ ଯାଞ୍ଚଗୁଡ଼ିକ ବିଫଳ ହୋଇଛି। ପୃଷ୍ଠା ସାଇଜ କମ୍ପାଟିବଲ ମୋଡ ବ୍ୟବହାର କରି ଏହି ଆପକୁ ଚଲାଯିବ। ସର୍ବୋତ୍ତମ କମ୍ପାଟିବିଲିଟୀ ପାଇଁ ଦୟାକରି 16 KB ସପୋର୍ଟ ସହ ଆପ୍ଲିକେସନକୁ ପୁଣି କମ୍ପାଇଲ କରନ୍ତୁ। ଅଧିକ ସୂଚନା ପାଇଁ <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>କୁ ଦେଖନ୍ତୁ"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"ସେବାର ସୁବିଧା ନାହିଁ।"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"ଆପଣ କଲର୍ ID ସେଟିଙ୍ଗ ବଦଳାଇପାରିବେ ନାହିଁ।"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g>କୁ ଡାଟା ସ୍ୱିଚ କରାଯାଇଛି"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> ଘଣ୍ଟାରେ"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> ଦିନରେ"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> ବର୍ଷରେ"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# ମିନିଟ ପୂର୍ବେ}other{# ମିନିଟ ପୂର୍ବେ}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ଘଣ୍ଟା ପୂର୍ବେ}other{# ଘଣ୍ଟା ପୂର୍ବେ}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ଦିନ ପୂର୍ବେ}other{# ଦିନ ପୂର୍ବେ}}"</string>
@@ -1671,7 +1700,7 @@
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"ହେଡଫୋନ୍"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"ସିଷ୍ଟମ"</string>
- <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"ବ୍ଲୁଟୂଥ୍ ଅଡିଓ"</string>
+ <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"ବ୍ଲୁଟୁଥ ଅଡିଓ"</string>
<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>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index c7b11e5..238354a 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ਪ੍ਰਤਿਬੰਧਿਤ ਕਰਨ ਲਈ ਕਾਲਰ ਆਈ.ਡੀ. ਪੂਰਵ-ਨਿਰਧਾਰਤ। ਅਗਲੀ ਕਾਲ: ਪ੍ਰਤਿਬੰਧਿਤ ਨਹੀਂ"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ਪ੍ਰਤਿਬੰਧਿਤ ਨਾ ਕਰਨ ਲਈ ਕਾਲਰ ਆਈ.ਡੀ. ਪੂਰਵ-ਨਿਰਧਾਰਤ। ਅਗਲੀ ਕਾਲ: ਪ੍ਰਤਿਬੰਧਿਤ"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ਪ੍ਰਤਿਬੰਧਿਤ ਨਾ ਕਰਨ ਲਈ ਕਾਲਰ ਆਈ.ਡੀ. ਪੂਰਵ-ਨਿਰਧਾਰਤ। ਅਗਲੀ ਕਾਲ: ਪ੍ਰਤਿਬੰਧਿਤ ਨਹੀਂ"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"ਇਹ ਐਪ 16 KB ਦੇ ਅਨੁਰੂਪ ਨਹੀਂ ਹੈ। APK ਦੀ ਇਕਸਾਰਤਾ ਦੀ ਜਾਂਚ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ। ਇਹ ਐਪ ਪੰਨੇ ਦੇ ਆਕਾਰ ਦੇ ਅਨੁਰੂਪ ਮੋਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਚੱਲੇਗੀ। ਬਿਹਤਰ ਅਨੁਰੂਪਤਾ ਲਈ, ਕਿਰਪਾ ਕਰਕੇ 16 KB ਵਾਲੇ ਪੰਨਿਆਂ ਨਾਲ ਐਪਲੀਕੇਸ਼ਨ ਦਾ ਮੁੜ-ਸੰਕਲਨ ਕਰੋ। ਹੋਰ ਜਾਣਕਾਰੀ ਲਈ, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> ਦੇਖੋ"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"ਇਹ ਐਪ 16 KB ਦੇ ਅਨੁਰੂਪ ਨਹੀਂ ਹੈ। ELF ਦੀ ਇਕਸਾਰਤਾ ਦੀ ਜਾਂਚ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ। ਇਹ ਐਪ ਪੰਨੇ ਦੇ ਆਕਾਰ ਦੇ ਅਨੁਰੂਪ ਮੋਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਚੱਲੇਗੀ। ਬਿਹਤਰ ਅਨੁਰੂਪਤਾ ਲਈ, ਕਿਰਪਾ ਕਰਕੇ 16 KB ਵਾਲੇ ਪੰਨਿਆਂ ਨਾਲ ਐਪਲੀਕੇਸ਼ਨ ਦਾ ਮੁੜ-ਸੰਕਲਨ ਕਰੋ। ਹੋਰ ਜਾਣਕਾਰੀ ਲਈ, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> ਦੇਖੋ"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"ਇਹ ਐਪ 16 KB ਦੇ ਅਨੁਰੂਪ ਨਹੀਂ ਹੈ। APK ਅਤੇ ELF ਦੀ ਇਕਸਾਰਤਾ ਦੀ ਜਾਂਚ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ। ਇਹ ਐਪ ਪੰਨੇ ਦੇ ਆਕਾਰ ਦੇ ਅਨੁਰੂਪ ਮੋਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਚੱਲੇਗੀ। ਬਿਹਤਰ ਅਨੁਰੂਪਤਾ ਲਈ, ਕਿਰਪਾ ਕਰਕੇ 16 KB ਵਾਲੇ ਪੰਨਿਆਂ ਨਾਲ ਐਪਲੀਕੇਸ਼ਨ ਦਾ ਮੁੜ-ਸੰਕਲਨ ਕਰੋ। ਹੋਰ ਜਾਣਕਾਰੀ ਲਈ, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> ਦੇਖੋ"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"ਸੇਵਾ ਪ੍ਰਬੰਧਿਤ ਨਹੀਂ ਹੈ।"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"ਤੁਸੀਂ ਕਾਲਰ ਆਈ.ਡੀ. ਸੈਟਿੰਗ ਨਹੀਂ ਬਦਲ ਸਕਦੇ।"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"ਡਾਟੇ ਨੂੰ <xliff:g id="CARRIERDISPLAY">%s</xliff:g> \'ਤੇ ਸਵਿੱਚ ਕੀਤਾ ਗਿਆ"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> ਘੰਟੇ ਵਿੱਚ"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> ਦਿਨ ਵਿੱਚ"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> ਸਾਲ ਵਿੱਚ"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# ਮਿੰਟ ਪਹਿਲਾਂ}one{# ਮਿੰਟ ਪਹਿਲਾਂ}other{# ਮਿੰਟ ਪਹਿਲਾਂ}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ਘੰਟਾ ਪਹਿਲਾਂ}one{# ਘੰਟਾ ਪਹਿਲਾਂ}other{# ਘੰਟੇ ਪਹਿਲਾਂ}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ਦਿਨ ਪਹਿਲਾਂ}one{# ਦਿਨ ਪਹਿਲਾਂ}other{# ਦਿਨ ਪਹਿਲਾਂ}}"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 7a7e159..3d71d7b 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -73,12 +73,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ID rozmówcy ustawiony jest domyślnie na „zastrzeżony”. Następne połączenie: nie zastrzeżony"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ID rozmówcy ustawiony jest domyślnie na „nie zastrzeżony”. Następne połączenie: zastrzeżony"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID rozmówcy ustawiony jest domyślnie na „nie zastrzeżony”. Następne połączenie: nie zastrzeżony"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Ta aplikacja nie jest zgodna z trybem 16 KB. Sprawdzenie zgodności pliku APK się nie powiodło. Ta aplikacja będzie działać w trybie zgodnym z rozmiarem strony. Aby zapewnić najlepszą zgodność, rekompiluj aplikację, żeby obsługiwała 16 KB. Więcej informacji znajdziesz na stronie <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Ta aplikacja nie jest zgodna z trybem 16 KB. Sprawdzenie zgodności ELF się nie powiodło. Ta aplikacja będzie działać w trybie zgodnym z rozmiarem strony. Aby zapewnić najlepszą zgodność, rekompiluj aplikację, żeby obsługiwała 16 KB. Więcej informacji znajdziesz na stronie <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Ta aplikacja nie jest zgodna z trybem 16 KB. Sprawdzenie zgodności pliku APK i ELF się nie powiodło. Ta aplikacja będzie działać w trybie zgodnym z rozmiarem strony. Aby zapewnić najlepszą zgodność, rekompiluj aplikację, żeby obsługiwała 16 KB. Więcej informacji znajdziesz na stronie <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Usługa nie jest świadczona."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Nie możesz zmienić ustawienia ID rozmówcy."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Przełączono mobilną transmisję danych na: <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1163,6 +1160,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"za <xliff:g id="COUNT">%d</xliff:g> godz."</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"za <xliff:g id="COUNT">%d</xliff:g> d."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"za <xliff:g id="COUNT">%d</xliff:g> r."</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minutę temu}few{# minuty temu}many{# minut temu}other{# minuty temu}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# godzinę temu}few{# godziny temu}many{# godzin temu}other{# godziny temu}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# dzień temu}few{# dni temu}many{# dni temu}other{# dnia temu}}"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index d332777..5ff383d 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"O identificador de chamadas assume o padrão de restrito. Próxima chamada: Não restrita"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"O identificador de chamadas assume o padrão de não restrito. Próxima chamada: Restrita"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"O identificador de chamadas assume o padrão de não restrito. Próxima chamada: Não restrita"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Este app não é compatível com 16 KB. Falha na verificação de alinhamento do APK. Este app será executado usando o modo compatível com o tamanho da página. Para melhorar a compatibilidade, é preciso recompilar o aplicativo com suporte a 16 KB. Para mais informações, consulte <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Este app não é compatível com 16 KB. Falha na verificação de alinhamento ELF. Este app será executado usando o modo compatível com o tamanho da página. Para melhorar a compatibilidade, é preciso recompilar o aplicativo com suporte a 16 KB. Para mais informações, consulte <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Este app não é compatível com 16 KB. Falha nas verificações de alinhamento do APK e do ELF. Este app será executado usando o modo compatível com o tamanho da página. Para melhorar a compatibilidade, é preciso recompilar o aplicativo com suporte a 16 KB. Para mais informações, consulte <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"O serviço não foi habilitado."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Não é possível alterar a configuração do identificador de chamadas."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Dados da <xliff:g id="CARRIERDISPLAY">%s</xliff:g> ativados"</string>
@@ -1162,6 +1159,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"em <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"em <xliff:g id="COUNT">%d</xliff:g> dias"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"em <xliff:g id="COUNT">%d</xliff:g>a"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"há <xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"há <xliff:g id="COUNT">%d</xliff:g>h"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"há <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"há <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"em <xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"em <xliff:g id="COUNT">%d</xliff:g>h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"em <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"em <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"há <xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"há <xliff:g id="COUNT">%d</xliff:g>h"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"há <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"há <xliff:g id="COUNT">%d</xliff:g> a"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuto atrás}one{# minuto atrás}many{# minutos atrás}other{# minutos atrás}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# hora atrás}one{# hora atrás}many{# horas atrás}other{# horas atrás}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# dia atrás}one{# dia atrás}many{# dias atrás}other{# dias atrás}}"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 8b1d408..38a071a 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1159,6 +1159,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"em <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"em <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"em <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"há <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"há <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"há <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"há <xliff:g id="COUNT">%d</xliff:g> ano(s)"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> ano(s)"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"dentro de <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"dentro de <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"dentro de <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"dentro de <xliff:g id="COUNT">%d</xliff:g> ano(s)"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"há <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"há <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"há <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"há <xliff:g id="COUNT">%d</xliff:g> ano(s)"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Há # minuto}many{Há # minutos}other{Há # minutos}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{há # hora}many{há # horas}other{há # horas}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Há # dia}many{Há # dias}other{Há # dias}}"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index d332777..5ff383d 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"O identificador de chamadas assume o padrão de restrito. Próxima chamada: Não restrita"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"O identificador de chamadas assume o padrão de não restrito. Próxima chamada: Restrita"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"O identificador de chamadas assume o padrão de não restrito. Próxima chamada: Não restrita"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Este app não é compatível com 16 KB. Falha na verificação de alinhamento do APK. Este app será executado usando o modo compatível com o tamanho da página. Para melhorar a compatibilidade, é preciso recompilar o aplicativo com suporte a 16 KB. Para mais informações, consulte <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Este app não é compatível com 16 KB. Falha na verificação de alinhamento ELF. Este app será executado usando o modo compatível com o tamanho da página. Para melhorar a compatibilidade, é preciso recompilar o aplicativo com suporte a 16 KB. Para mais informações, consulte <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Este app não é compatível com 16 KB. Falha nas verificações de alinhamento do APK e do ELF. Este app será executado usando o modo compatível com o tamanho da página. Para melhorar a compatibilidade, é preciso recompilar o aplicativo com suporte a 16 KB. Para mais informações, consulte <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"O serviço não foi habilitado."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Não é possível alterar a configuração do identificador de chamadas."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Dados da <xliff:g id="CARRIERDISPLAY">%s</xliff:g> ativados"</string>
@@ -1162,6 +1159,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"em <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"em <xliff:g id="COUNT">%d</xliff:g> dias"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"em <xliff:g id="COUNT">%d</xliff:g>a"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"há <xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"há <xliff:g id="COUNT">%d</xliff:g>h"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"há <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"há <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"em <xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"em <xliff:g id="COUNT">%d</xliff:g>h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"em <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"em <xliff:g id="COUNT">%d</xliff:g> a"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"há <xliff:g id="COUNT">%d</xliff:g>min"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"há <xliff:g id="COUNT">%d</xliff:g>h"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"há <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"há <xliff:g id="COUNT">%d</xliff:g> a"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuto atrás}one{# minuto atrás}many{# minutos atrás}other{# minutos atrás}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# hora atrás}one{# hora atrás}many{# horas atrás}other{# horas atrás}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# dia atrás}one{# dia atrás}many{# dias atrás}other{# dias atrás}}"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index ed97431..79bfcf4 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ID-ul apelantului este restricționat în mod prestabilit. Apelul următor: nerestricționat"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ID-ul apelantului este nerestricționat în mod prestabilit. Apelul următor: Restricționat."</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID-ul apelantului este nerestricționat în mod prestabilit. Apelul următor: nerestricționat"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Această aplicație nu este compatibilă cu 16 KB. Verificarea alinierii APK nu a reușit. Aplicația va rula folosind modul compatibil cu dimensiunea paginii. Pentru cea mai bună compatibilitate, recompilează aplicația cu suport pentru 16 KB. Pentru mai multe informații, accesează <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Această aplicație nu este compatibilă cu 16 KB. Verificarea alinierii ELF nu a reușit. Aplicația va rula folosind modul compatibil cu dimensiunea paginii. Pentru cea mai bună compatibilitate, recompilează aplicația cu suport pentru 16 KB. Pentru mai multe informații, accesează <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Această aplicație nu este compatibilă cu 16 KB. Verificările de aliniere APK și ELF nu au reușit. Aplicația va rula folosind modul compatibil cu dimensiunea paginii. Pentru cea mai bună compatibilitate, recompilează aplicația cu suport pentru 16 KB. Pentru mai multe informații, accesează <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Nu se asigură accesul la acest serviciu."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Nu poți modifica setarea pentru ID-ul apelantului."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"S-a trecut la datele mobile <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"în <xliff:g id="COUNT">%d</xliff:g> ore"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"în <xliff:g id="COUNT">%d</xliff:g> z"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"în <xliff:g id="COUNT">%d</xliff:g> ani"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Acum # minut}few{Acum # minute}other{Acum # de minute}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Acum # oră}few{Acum # ore}other{Acum # de ore}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Acum # zi}few{Acum # zile}other{Acum # de zile}}"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 9b3a719..2631dab 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -73,12 +73,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Идентификация абонента по умолчанию запрещена. След. вызов: разрешена"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Идентификация абонента по умолчанию не запрещена. След. вызов: запрещена"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Идентификация абонента по умолчанию не запрещена. След. вызов: разрешена"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Это приложение не поддерживает страничную память объемом 16 КБ. Не удалось проверить выравнивание данных в APK-файле. Приложение будет запущено в режиме совместимости. Чтобы улучшить совместимость, заново скомпилируйте приложение для поддержки страничной памяти объемом 16 КБ. Подробнее: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Это приложение не поддерживает страничную память объемом 16 КБ. Не удалось проверить выравнивание данных в ELF-файле. Приложение будет запущено в режиме совместимости. Чтобы улучшить совместимость, заново скомпилируйте приложение для поддержки страничной памяти объемом 16 КБ. Подробнее: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Это приложение не поддерживает страничную память объемом 16 КБ. Не удалось проверить выравнивание данных в APK- и ELF-файлах. Приложение будет запущено в режиме совместимости. Чтобы улучшить совместимость, заново скомпилируйте приложение для поддержки страничной памяти объемом 16 КБ. Подробнее: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Услуга не предоставляется."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Невозможно изменить параметр идентификатора вызывающего абонента."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Используется мобильный интернет <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1163,6 +1160,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"через <xliff:g id="COUNT">%d</xliff:g> ч."</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"через <xliff:g id="COUNT">%d</xliff:g> дн."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"через <xliff:g id="COUNT">%d</xliff:g> г."</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# минуту назад}one{# минуту назад}few{# минуты назад}many{# минут назад}other{# минуты назад}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# час назад}one{# час назад}few{# часа назад}many{# часов назад}other{# часа назад}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# день назад}one{# день назад}few{# дня назад}many{# дней назад}other{# дня назад}}"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index a02cf02..99b925a 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"අමතන්නාගේ ID සුපුරුදු අනුව සීමා වී ඇත. මීළඟ ඇමතුම: සීමා කර නැත"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"අමතන්නාගේ ID සුපුරුදු අනුව සීමා වී නැත. මීළඟ ඇමතුම: සීමා කර ඇත"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"අමතන්නාගේ ID සුපුරුදු අනුව සීමා වී නැත. මීළඟ ඇමතුම: සීමා කර ඇත"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"මෙම යෙදුම 16 KB අනුකූල නොවේ. APK පෙළගැස්වීමේ පරීක්ෂාව අසමත් විය. මෙම යෙදුම පිටු ප්රමාණයට ගැළපෙන ප්රකාරය භාවිතයෙන් ධාවනය වනු ඇත. හොඳම ගැළපුම සඳහා, 16 KB සහාය ඇතිව යෙදුම නැවත සම්පාදනය කරන්න. වැඩිපුර තොරතුරු සඳහා, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> බලන්න"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"මෙම යෙදුම 16 KB අනුකූල නොවේ. ELF පෙළගැස්වීමේ පරීක්ෂාව අසමත් විය. මෙම යෙදුම පිටු ප්රමාණයට ගැළපෙන ප්රකාරය භාවිතයෙන් ධාවනය වනු ඇත. හොඳම ගැළපුම සඳහා, 16 KB සහාය ඇතිව යෙදුම නැවත සම්පාදනය කරන්න. වැඩිපුර තොරතුරු සඳහා, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> බලන්න"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"මෙම යෙදුම 16 KB අනුකූල නොවේ. APK සහ ELF පෙළගැස්වීමේ පරීක්ෂාවන් අසමත් විය. මෙම යෙදුම පිටු ප්රමාණයට ගැළපෙන ප්රකාරය භාවිතයෙන් ධාවනය වනු ඇත. හොඳම ගැළපුම සඳහා, 16 KB සහාය ඇතිව යෙදුම නැවත සම්පාදනය කරන්න. වැඩිපුර තොරතුරු සඳහා, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> බලන්න"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"සේවාවන් සපයා නැත."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"අමතන්නාගේ ID සැකසීම ඔබට වෙනස්කල නොහැක."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"දත්ත <xliff:g id="CARRIERDISPLAY">%s</xliff:g> වෙත මාරු කරන ලදි"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"පැ<xliff:g id="COUNT">%d</xliff:g>කින්"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"දි<xliff:g id="COUNT">%d</xliff:g>කින්"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"ව<xliff:g id="COUNT">%d</xliff:g>කින්"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{මිනිත්තු #කට පෙර}one{මිනිත්තු #කට පෙර}other{මිනිත්තු #කට පෙර}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{පැය #කට පෙර}one{පැය #කට පෙර}other{පැය #කට පෙර}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{දින #කට පෙර}one{දින #කට පෙර}other{දින #කට පෙර}}"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index fd2c727..3277781 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -73,12 +73,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"V predvolenom nastavení je identifikácia volajúceho obmedzená. Ďalší hovor: Bez obmedzenia"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"V predvolenom nastavení nie je identifikácia volajúceho obmedzená. Ďalší hovor: Obmedzené"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"V predvolenom nastavení nie je identifikácia volajúceho obmedzená. Ďalší hovor: Bez obmedzenia"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Táto aplikácia nie je kompatibilná s režimom 16 kB. Nepodarilo sa skontrolovať zarovnanie súboru APK. Táto aplikácia bude spustená v režime kompatibilnom s veľkosťou stránky. Ak chcete dosiahnuť najlepšiu kompatibilitu, znova skompilujte aplikáciu s podporou 16 kB. Viac sa dozviete na <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Táto aplikácia nie je kompatibilná s režimom 16 kB. Nepodarilo sa skontrolovať zarovnanie formátu ELF. Táto aplikácia bude spustená v režime kompatibilnom s veľkosťou stránky. Ak chcete dosiahnuť najlepšiu kompatibilitu, znova skompilujte aplikáciu s podporou 16 kB. Viac sa dozviete na <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Táto aplikácia nie je kompatibilná s režimom 16 kB. Nepodarilo sa skontrolovať zarovnanie súboru APK a formátu ELF. Táto aplikácia bude spustená v režime kompatibilnom s veľkosťou stránky. Ak chcete dosiahnuť najlepšiu kompatibilitu, znova skompilujte aplikáciu s podporou 16 kB. Viac sa dozviete na <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Služba nie je poskytovaná."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Nemôžete meniť nastavenie identifikácie volajúcich."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Dátové pripojenie bolo prepnuté na operátora <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1163,6 +1160,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"o <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"o <xliff:g id="COUNT">%d</xliff:g> d."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"o <xliff:g id="COUNT">%d</xliff:g> r."</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Pred # minútou}few{Pred # minútami}many{Pred # minúty}other{Pred # minútami}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Pred # hodinou}few{Pred # hodinami}many{Pred # hodiny}other{Pred # hodinami}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Pred # dňom}few{Pred # dňami}many{Pred # dňa}other{Pred # dňami}}"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 51803b0..e7e96f5 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1160,6 +1160,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"čez <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"čez <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"čez <xliff:g id="COUNT">%d</xliff:g> l"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"pred <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"pred <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"pred <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"pred <xliff:g id="COUNT">%d</xliff:g> l"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> l"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"čez <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"čez <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"čez <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"čez <xliff:g id="COUNT">%d</xliff:g> l"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"pred <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"pred <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"pred <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"pred <xliff:g id="COUNT">%d</xliff:g> l"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Pred # minuto}one{Pred # minuto}two{Pred # minutama}few{Pred # minutami}other{Pred # minutami}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Pred # uro}one{Pred # uro}two{Pred # urama}few{Pred # urami}other{Pred # urami}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Pred # dnevom}one{Pred # dnevom}two{Pred # dnevoma}few{Pred # dnevi}other{Pred # dnevi}}"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 995910a..44981bb 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ID-ja e telefonuesit kalon me paracaktim në listën e të telefonuesve të kufizuar. Telefonata e radhës: e pakufizuar!"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ID-ja e telefonuesit kalon me paracaktim në listën e të telefonuesve të pakufizuar. Telefonata e radhës: e kufizuar!"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID-ja e telefonuesit kalon me paracaktim në listën e të telefonuesve të pakufizuar. Telefonata e radhës: e pakufizuar!"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Ky aplikacion nuk është i përputhshëm me modalitetin 16 KB. Kontrolli i drejtvendosjes për APK dështoi. Ky aplikacion do të ekzekutohet duke përdorur modalitetin në përputhje me madhësinë e faqes. Për përputhshmërinë më të mirë, ripërpiloje aplikacionin me mbështetjen e modalitetit 16 KB. Për më shumë informacione, shiko <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Ky aplikacion nuk është i përputhshëm me modalitetin 16 KB. Kontrolli i drejtvendosjes për ELF dështoi. Ky aplikacion do të ekzekutohet duke përdorur modalitetin në përputhje me madhësinë e faqes. Për përputhshmërinë më të mirë, ripërpiloje aplikacionin me mbështetjen e modalitetit 16 KB. Për më shumë informacione, shiko <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Ky aplikacion nuk është i përputhshëm me modalitetin 16 KB. Kontrollet e drejtvendosjes për APK dhe ELF dështuan. Ky aplikacion do të ekzekutohet duke përdorur modalitetin në përputhje me madhësinë e faqes. Për përputhshmërinë më të mirë, ripërpiloje aplikacionin me mbështetjen e modalitetit 16 KB. Për më shumë informacione, shiko <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Shërbimi nuk është përgatitur."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Nuk mund ta ndryshosh cilësimin e ID-së së telefonuesit."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Të dhënat u kaluan te <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"për <xliff:g id="COUNT">%d</xliff:g> orë"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"për <xliff:g id="COUNT">%d</xliff:g> ditë"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"për <xliff:g id="COUNT">%d</xliff:g> vit"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minutë më parë}other{# minuta më parë}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# orë më parë}other{# orë më parë}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ditë më parë}other{# ditë më parë}}"</string>
@@ -1671,7 +1700,7 @@
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Kufjet"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistemi"</string>
- <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"Audioja e Bluetooth-it"</string>
+ <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"Audioja me Bluetooth"</string>
<string name="wireless_display_route_description" msgid="8297563323032966831">"Ekran wireless"</string>
<string name="media_route_button_content_description" msgid="2299223698196869956">"Transmeto"</string>
<string name="media_route_chooser_title" msgid="6646594924991269208">"Lidhu me pajisjen"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 7ce7b7b..cc2248c 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -72,12 +72,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ИД позиваоца је подразумевано ограничен. Следећи позив: Није ограничен."</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ИД позиваоца подразумевано није ограничен. Следећи позив: ограничен."</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ИД позиваоца подразумевано није ограничен. Следећи позив: Није ограничен."</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Ова апликација није компатибилна са величином странице од 16 kB. Провера усклађености APK-а није успела. Ова апликација ће радити помоћу режима компатибилног са величином странице. Да бисте обезбедили најбољу компатибилност, поново компајлирајте апликацију са подршком за величину странице од 16 kB. Више информација потражите на <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Ова апликација није компатибилна са величином странице од 16 kB. Провера усклађености ELF-а није успела. Ова апликација ће радити помоћу режима компатибилног са величином странице. Да бисте обезбедили најбољу компатибилност, поново компајлирајте апликацију са подршком за величину странице од 16 kB. Више информација потражите на <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Ова апликација није компатибилна са величином странице од 16 kB. Провере усклађености APK-а и ELF-а нису успеле. Ова апликација ће радити помоћу режима компатибилног са величином странице. Да бисте обезбедили најбољу компатибилност, поново компајлирајте апликацију са подршком за величину странице од 16 kB. Више информација потражите на <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Услуга није добављена."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Не можете да промените подешавање ИД-а корисника."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Мобилни подаци су пребачени на оператера <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1162,6 +1159,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"за <xliff:g id="COUNT">%d</xliff:g> с"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"за <xliff:g id="COUNT">%d</xliff:g> д"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"за <xliff:g id="COUNT">%d</xliff:g> год"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Пре # минут}one{Пре # минут}few{Пре # минута}other{Пре # минута}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Пре # сат}one{Пре # сат}few{Пре # сата}other{Пре # сати}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Пре # дан}one{Пре # дан}few{Пре # дана}other{Пре # дана}}"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index a9a6067..4b22b7d 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Nummerpresentatörens standardinställning är blockerad. Nästa samtal: Inte blockerad"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Nummerpresentatörens standardinställning är inte blockerad. Nästa samtal: Blockerad"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Nummerpresentatörens standardinställning är inte blockerad. Nästa samtal: Inte blockerad"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Den här appen är inte kompatibel med 16 KB. APK-justeringskontrollen misslyckades. Appen körs i kompatibilitetsläget för sidstorlek. För bästa möjliga kompatibilitet bör du kompilera appen igen med stöd för 16 kB. Läs mer på <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Den här appen är inte kompatibel med 16 KB. ELF-justeringskontrollen misslyckades. Appen körs i kompatibilitetsläget för sidstorlek. För bästa möjliga kompatibilitet bör du kompilera appen igen med stöd för 16 kB. Läs mer på <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Den här appen är inte kompatibel med 16 kB. APK- och ELF-justeringskontrollerna misslyckades. Appen körs i kompatibilitetsläget för sidstorlek. För bästa möjliga kompatibilitet bör du kompilera appen igen med stöd för 16 kB. Läs mer på <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Tjänsten är inte etablerad."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Det går inte att ändra inställningen för nummerpresentatör."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Du har bytt mobildata till <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"om <xliff:g id="COUNT">%d</xliff:g> tim"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"om <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"om <xliff:g id="COUNT">%d</xliff:g> år"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"för <xliff:g id="COUNT">%d</xliff:g> m sedan"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"för <xliff:g id="COUNT">%d</xliff:g> h sedan"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"för <xliff:g id="COUNT">%d</xliff:g> d sedan"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"för <xliff:g id="COUNT">%d</xliff:g> år sedan"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g> år"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"om <xliff:g id="COUNT">%d</xliff:g> min"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"om <xliff:g id="COUNT">%d</xliff:g> h"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"om <xliff:g id="COUNT">%d</xliff:g> d"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"om <xliff:g id="COUNT">%d</xliff:g> år"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"för <xliff:g id="COUNT">%d</xliff:g> min sedan"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"för <xliff:g id="COUNT">%d</xliff:g> h sedan"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"för <xliff:g id="COUNT">%d</xliff:g> d sedan"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"för <xliff:g id="COUNT">%d</xliff:g> år sedan"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{För # minut sedan}other{För # minuter sedan}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{För # timme sedan}other{För # timmar sedan}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{För # dag sedan}other{För # dagar sedan}}"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 4d0d7dc..a7d4d86 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Chaguomsingi za kitambulisho cha mpigaji simu huwa kuzuiwa. Simu ifuatayo: Haijazuiliwa"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Chaguomsingi za ID ya mpigaji simu za kutozuia. Simu ifuatayo:Imezuiliwa"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Chaguomsingi za ID ya mpigaji simu za kutozuia. Simu ifuatayo: Haijazuiliwa"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Programu hii haioani na ukubwa wa ukurasa wa KB 16. Imeshindwa kukagua mpangilio wa APK. Programu hii itaendeshwa katika hali inayooana na ukubwa wa ukurasa. Tafadhali rekebisha mipangilio ya programu ijumuishe KB 16 ili ioane vizuri. Tembelea <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> upate maelezo zaidi"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Programu hii haioani na ukubwa wa ukurasa wa KB 16. Imeshindwa kukagua mpangilio wa ELF. Programu hii itaendeshwa katika hali inayooana na ukubwa wa ukurasa. Tafadhali rekebisha mipangilio ya programu ijumuishe KB 16 ili ioane vizuri. Tembelea <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> upate maelezo zaidi"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Programu hii haioani na ukubwa wa ukurasa wa KB 16. Imeshindwa kukagua mpangilio wa APK na ELF. Programu hii itaendeshwa katika hali inayooana na ukubwa wa ukurasa. Tafadhali rekebisha mipangilio ya programu ijumuishe KB 16 ili ioane vizuri. Tembelea <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> upate maelezo zaidi"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Huduma haitathminiwi."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Hauwezi kubadilisha mpangilio wa kitambulisho cha anayepiga."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Sasa unatumia data ya mtandao wa <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"baada ya saa <xliff:g id="COUNT">%d</xliff:g>"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"baada ya siku <xliff:g id="COUNT">%d</xliff:g>"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"baada ya mwaka <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Dakika # iliyopita}other{Dakika # zilizopita}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Saa # iliyopita}other{Saa # zilizopita}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Siku # iliyopita}other{Siku # zilizopita}}"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 96e62f2..6107dbe 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"அழைப்பாளர் ஐடி ஆனது வரையறுக்கப்பட்டது என்பதற்கு இயல்பாக அமைக்கப்பட்டது. அடுத்த அழைப்பு: வரையறுக்கப்படவில்லை"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"அழைப்பாளர் ஐடி ஆனது வரையறுக்கப்படவில்லை என்பதற்கு இயல்பாக அமைக்கப்பட்டது. அடுத்த அழைப்பு: வரையறுக்கப்பட்டது"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"அழைப்பாளர் ஐடி ஆனது வரையறுக்கப்படவில்லை என்பதற்கு இயல்பாக அமைக்கப்பட்டது. அடுத்த அழைப்பு: வரையறுக்கப்படவில்லை"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"இந்த ஆப்ஸ் 16 KB இணக்கத்தன்மை வாய்ந்தது அல்ல. APK சீரமைவைச் சரிபார்க்க முடியவில்லை. பக்க அளவுடனான இணக்கத்தன்மைப் பயன்முறையைப் பயன்படுத்தி இந்த ஆப்ஸ் இயக்கப்படும். சிறந்த இணக்கத்தன்மைக்கு, ஆப்ஸை 16 KB ஆதரவுடன் ரீ-கம்பைல் செய்யவும். கூடுதல் தகவல்களுக்கு, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> எனும் பக்கத்தைப் பார்க்கவும்."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"இந்த ஆப்ஸ் 16 KB இணக்கத்தன்மை வாய்ந்தது அல்ல. ELF சீரமைவைச் சரிபார்க்க முடியவில்லை. பக்க அளவுடனான இணக்கத்தன்மைப் பயன்முறையைப் பயன்படுத்தி இந்த ஆப்ஸ் இயக்கப்படும். சிறந்த இணக்கத்தன்மைக்கு, ஆப்ஸை 16 KB ஆதரவுடன் ரீ-கம்பைல் செய்யவும். கூடுதல் தகவல்களுக்கு, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> எனும் பக்கத்தைப் பார்க்கவும்."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"இந்த ஆப்ஸ் 16 KB இணக்கத்தன்மை வாய்ந்தது அல்ல. APK மற்றும் ELF சீரமைவுகளைச் சரிபார்க்க முடியவில்லை. பக்க அளவுடனான இணக்கத்தன்மைப் பயன்முறையைப் பயன்படுத்தி இந்த ஆப்ஸ் இயக்கப்படும். சிறந்த இணக்கத்தன்மைக்கு, ஆப்ஸை 16 KB ஆதரவுடன் ரீ-கம்பைல் செய்யவும். கூடுதல் தகவல்களுக்கு, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> எனும் பக்கத்தைப் பார்க்கவும்."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"சேவை ஒதுக்கப்படவில்லை."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"அழைப்பாளர் ஐடி அமைப்பை மாற்ற முடியாது."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"டேட்டா <xliff:g id="CARRIERDISPLAY">%s</xliff:g>க்கு மாற்றப்பட்டது"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> மணிநேரத்தில்"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> நாட்களில்"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> ஆண்டில்"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# நிமிடத்திற்கு முன்பு}other{# நிமிடங்களுக்கு முன்பு}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# மணிநேரத்திற்கு முன்பு}other{# மணிநேரத்திற்கு முன்பு}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# நாளுக்கு முன்பு}other{# நாட்களுக்கு முன்பு}}"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 9495a96..53ef42a 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"కాలర్ ID ఆటోమేటిక్లపై పరిమితి ఉంటుంది. తర్వాత కాల్: పరిమితి లేదు"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"కాలర్ ID ఆటోమేటిక్లపై పరిమితి లేదు. తర్వాత కాల్: పరిమితి ఉంటుంది"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"కాలర్ ID ఆటోమేటిక్లపై పరిమితి లేదు. తర్వాత కాల్: పరిమితి లేదు"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"ఈ యాప్ 16 KBకి అనుకూలమైనది కాదు. APK అమరిక చెకింగ్ విఫలమైంది. ఈ యాప్ పేజీ సైజ్ అనుకూల మోడ్ను ఉపయోగించి రన్ అవుతుంది. సరైన అనుకూలత కోసం, దయచేసి 16 KB సపోర్ట్తో అప్లికేషన్ను మళ్లీ కంపైల్ చేయండి. మరింత సమాచారం కోసం, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> ను చూడండి"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"ఈ యాప్ 16 KBకి అనుకూలమైనది కాదు. ELF అమరిక చెకింగ్ విఫలమైంది. ఈ యాప్ పేజీ సైజ్ అనుకూల మోడ్ను ఉపయోగించి రన్ అవుతుంది. సరైన అనుకూలత కోసం, దయచేసి 16 KB సపోర్ట్తో అప్లికేషన్ను మళ్లీ కంపైల్ చేయండి. మరింత సమాచారం కోసం, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> ను చూడండి"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"ఈ యాప్ 16 KBకి అనుకూలమైనది కాదు. APK, ELF అమరిక చెకప్ దశలు విఫలమయ్యాయి. ఈ యాప్ పేజీ సైజ్ అనుకూల మోడ్ను ఉపయోగించి రన్ అవుతుంది. సరైన అనుకూలత కోసం, దయచేసి 16 KB సపోర్ట్తో అప్లికేషన్ను మళ్లీ కంపైల్ చేయండి. మరింత సమాచారం కోసం, <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> ను చూడండి"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"సేవ కేటాయించబడలేదు."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"మీరు కాలర్ ID సెట్టింగ్ను మార్చలేరు."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"డేటాను <xliff:g id="CARRIERDISPLAY">%s</xliff:g>కు స్విచ్ చేశారు"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g>గంటలో"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>రోజులో"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g>సంవత్సరంలో"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# నిమిషం క్రితం}other{# నిమిషాల క్రితం}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# గంట క్రితం}other{# గంటల క్రితం}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# రోజు క్రితం}other{# రోజుల క్రితం}}"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 86931c0..43b1423 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"หมายเลขผู้โทรได้รับการตั้งค่าเริ่มต้นเป็นถูกจำกัด การโทรครั้งต่อไป: ไม่จำกัด"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"หมายเลขผู้โทรได้รับการตั้งค่าเริ่มต้นเป็นไม่จำกัด การโทรครั้งต่อไป: ถูกจำกัด"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"หมายเลขผู้โทรได้รับการตั้งค่าเริ่มต้นเป็นไม่จำกัด การโทรครั้งต่อไป: ไม่จำกัด"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"แอปนี้ไม่รองรับขนาดหน้า 16 KB ผลการตรวจสอบการจัดตำแหน่ง APK คือไม่ผ่าน ด้วยเหตุนี้ แอปจะทำงานโดยใช้โหมดการใช้งานร่วมกับขนาดหน้าได้ เพื่อให้ใช้งานร่วมกันได้อย่างดีที่สุด โปรดคอมไพล์แอปพลิเคชันอีกครั้งโดยมีการรองรับขนาดหน้า 16 KB ดูข้อมูลเพิ่มเติมได้ที่ <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"แอปนี้ไม่รองรับขนาดหน้า 16 KB ผลการตรวจสอบการจัดตำแหน่ง ELF คือไม่ผ่าน ด้วยเหตุนี้ แอปจะทำงานโดยใช้โหมดการใช้งานร่วมกับขนาดหน้าได้ เพื่อให้ใช้งานร่วมกันได้อย่างดีที่สุด โปรดคอมไพล์แอปพลิเคชันอีกครั้งโดยมีการรองรับขนาดหน้า 16 KB ดูข้อมูลเพิ่มเติมได้ที่ <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"แอปนี้ไม่รองรับขนาดหน้า 16 KB ผลการตรวจสอบการจัดตำแหน่งของ APK และ ELF คือไม่ผ่าน ด้วยเหตุนี้ แอปจะทำงานโดยใช้โหมดการใช้งานร่วมกับขนาดหน้าได้ เพื่อให้ใช้งานร่วมกันได้อย่างดีที่สุด โปรดคอมไพล์แอปพลิเคชันอีกครั้งโดยมีการรองรับขนาดหน้า 16 KB ดูข้อมูลเพิ่มเติมได้ที่ <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"ไม่มีการนำเสนอบริการ"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"คุณไม่สามารถเปลี่ยนการตั้งค่าหมายเลขผู้โทร"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"เปลี่ยนไปใช้อินเทอร์เน็ตมือถือของ <xliff:g id="CARRIERDISPLAY">%s</xliff:g> แล้ว"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"ใน <xliff:g id="COUNT">%d</xliff:g> ชม."</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"ใน <xliff:g id="COUNT">%d</xliff:g> วัน"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"ใน <xliff:g id="COUNT">%d</xliff:g> ปี"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# นาทีที่ผ่านมา}other{# นาทีที่ผ่านมา}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ชั่วโมงที่ผ่านมา}other{# ชั่วโมงที่ผ่านมา}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# วันที่ผ่านมา}other{# วันที่ผ่านมา}}"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 461050e..1dbd00b 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Nade-default sa pinaghihigpitan ang Caller ID. Susunod na tawag: hindi pinaghihigpitan"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Naka-default sa hindi pinaghihigpitan ang Caller ID. Susunod na tawag: Pinaghihigpitan"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Naka-default na hindi pinaghihigpitan ang Caller ID. Susunod na tawag: Hindi pinaghihigpitan"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Hindi 16 KB compatible ang app na ito. Hindi pumasa ang alignment check sa APK. Patatakbuhin ang app na ito gamit ang page size compatible mode. Para sa pinakamahusay na compatibility, paki-recompile ang application nang may suporta sa 16 KB. Para sa higit pang impormasyon, tingnan ang <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Hindi 16 KB compatible ang app na ito. Hindi pumasa ang alignment check sa ELF. Patatakbuhin ang app na ito gamit ang page size compatible mode. Para sa pinakamahusay na compatibility, paki-recompile ang application nang may suporta sa 16 KB. Para sa higit pang impormasyon, tingnan ang <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Hindi 16 KB compatible ang app na ito. Hindi pumasa ang mga alignment check sa APK at ELF. Patatakbuhin ang app na ito gamit ang page size compatible mode. Para sa pinakamahusay na compatibility, paki-recompile ang application nang may suporta sa 16 KB. Para sa higit pang impormasyon, tingnan ang <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Hindi naprobisyon ang serbisyo."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Hindi mo mababago ang setting ng caller ID."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Nailipat sa <xliff:g id="CARRIERDISPLAY">%s</xliff:g> ang data"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"sa <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"sa <xliff:g id="COUNT">%d</xliff:g>d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"sa <xliff:g id="COUNT">%d</xliff:g>y"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuto ang nakalipas}one{# minuto ang nakalipas}other{# na minuto ang nakalipas}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# oras ang nakalipas}one{# oras ang nakalipas}other{# na oras ang nakalipas}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# araw ang nakalipas}one{# araw ang nakalipas}other{# na araw ang nakalipas}}"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index bc90fe1..3777f04 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Arayan kimliği varsayılanları kısıtlanmıştır. Sonraki çağrı: Kısıtlanmamış"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Arayan kimliği varsayılanları kısıtlanmamıştır. Sonraki çağrı: Kısıtlanmış"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Arayan kimliği varsayılanları kısıtlanmamıştır. Sonraki çağrı: Kısıtlanmamış"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Bu uygulama 16 KB ile uyumlu değil. APK uyumluluk kontrolü başarısız oldu. Bu uygulama, sayfa boyutuna uyumlu mod kullanılarak çalıştırılacak. En iyi uyumluluk için lütfen uygulamayı 16 KB desteğiyle yeniden derleyin. Daha fazla bilgi için <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> adresini ziyaret edin."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Bu uygulama 16 KB ile uyumlu değil. ELF uyumluluk kontrolü başarısız oldu. Bu uygulama, sayfa boyutuna uyumlu mod kullanılarak çalıştırılacak. En iyi uyumluluk için lütfen uygulamayı 16 KB desteğiyle yeniden derleyin. Daha fazla bilgi için <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> adresini ziyaret edin."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Bu uygulama 16 KB ile uyumlu değil. APK ve ELF uyumluluk kontrolleri başarısız oldu. Bu uygulama, sayfa boyutuna uyumlu mod kullanılarak çalıştırılacak. En iyi uyumluluk için lütfen uygulamayı 16 KB desteğiyle yeniden derleyin. Daha fazla bilgi için <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> adresini ziyaret edin."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Hizmet sağlanamadı."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Arayanın kimliği ayarını değiştiremezsiniz."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Veriler şuraya aktarıldı: <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> saat içinde"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> gün içinde"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> yıl içinde"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# dakika önce}other{# dakika önce}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# saat önce}other{# saat önce}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# gün önce}other{# gün önce}}"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 8b5a321..3d603e3 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -73,12 +73,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Ідентиф. абонента за умовч. обмеж. Наст. дзвінок: не обмеж."</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Ідентиф. абонента за умовч. не обмеж. Наст. дзвінок: обмеж."</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Ідентиф. абонента за умовч. не обмеж. Наст. дзвінок: не обмежений"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Цей додаток несумісний зі сторінками пам’яті з розміром 16 КБ. Не вдалося виконати перевірку вирівнювання APK. Цей додаток працюватиме в режимі сумісності з розміром сторінки. Для оптимальної сумісності перекомпілюйте додаток так, щоб він підтримував сторінки пам’яті з розміром 16 КБ. Щоб дізнатися більше, перегляньте статтю <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Цей додаток несумісний зі сторінками пам’яті з розміром 16 КБ. Не вдалося виконати перевірку вирівнювання ELF. Цей додаток працюватиме в режимі сумісності з розміром сторінки. Для оптимальної сумісності перекомпілюйте додаток так, щоб він підтримував сторінки пам’яті з розміром 16 КБ. Щоб дізнатися більше, перегляньте статтю <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Цей додаток несумісний зі сторінками пам’яті з розміром 16 КБ. Не вдалося виконати перевірку вирівнювання APK і ELF. Цей додаток працюватиме в режимі сумісності з розміром сторінки. Для оптимальної сумісності перекомпілюйте додаток так, щоб він підтримував сторінки пам’яті з розміром 16 КБ. Щоб дізнатися більше, перегляньте статтю <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Службу не ініціалізовано."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Ви не можете змінювати налаштування ідентифікатора абонента."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Мобільний Інтернет переключено на <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1163,6 +1160,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"через <xliff:g id="COUNT">%d</xliff:g> год"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"через <xliff:g id="COUNT">%d</xliff:g> дн."</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"через <xliff:g id="COUNT">%d</xliff:g> р."</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# хвилину тому}one{# хвилину тому}few{# хвилини тому}many{# хвилин тому}other{# хвилини тому}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# годину тому}one{# годину тому}few{# години тому}many{# годин тому}other{# години тому}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# день тому}one{# день тому}few{# дні тому}many{# днів тому}other{# дня тому}}"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index ae81328..8a9c661 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"کالر ID کی ڈیفالٹ ترتیب محدود کردہ ہے۔ اگلی کال: غیر محدود کردہ"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"کالر ID کی ڈیفالٹ ترتیب غیر محدود کردہ ہے۔ اگلی کال: محدود کردہ"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"کالر ID کی ڈیفالٹ ترتیب غیر محدود کردہ ہے۔ اگلی کال: غیر محدود کردہ"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"یہ ایپ 16 KB کے موافق نہیں ہے۔ APK الائنمنٹ چیک ناکام ہو گیا۔ یہ ایپ صفحہ کے سائز کے ساتھ موافقت رکھنے والے موڈ کے ساتھ چلے گی۔ بہترین موافقت کے لیے، براہ کرم 16 KB کے سپورٹ کے ساتھ ایپلیکیشن کو دوبارہ مرتب کریں۔ مزید معلومات کے لیے، <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> دیکھیں"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"یہ ایپ 16 KB کے موافق نہیں ہے۔ ELF الائنمنٹ چیک ناکام ہو گیا۔ یہ ایپ صفحہ کے سائز کے ساتھ موافقت رکھنے والے موڈ کے ساتھ چلے گی۔ بہترین موافقت کے لیے، براہ کرم 16 KB کے سپورٹ کے ساتھ ایپلیکیشن کو دوبارہ مرتب کریں۔ مزید معلومات کے لیے، <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> دیکھیں"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"یہ ایپ 16 KB کے موافق نہیں ہے۔ APK اور ELF الائنمنٹ چیکس ناکام ہو گئے۔ یہ ایپ صفحہ کے سائز کے ساتھ موافقت رکھنے والے موڈ کے ساتھ چلے گی۔ بہترین موافقت کے لیے، براہ کرم 16 KB کے سپورٹ کے ساتھ ایپلیکیشن کو دوبارہ مرتب کریں۔ مزید معلومات کے لیے، <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a> دیکھیں"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"سروس فراہم نہیں کی گئی۔"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"آپ کالر ID کی ترتیبات تبدیل نہیں کر سکتے ہیں۔"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"ڈیٹا <xliff:g id="CARRIERDISPLAY">%s</xliff:g> پر سوئچ کیا گیا"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> گھنٹے میں"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> دن میں"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> سال میں"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# منٹ پہلے}other{# منٹ پہلے}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# گھنٹہ پہلے}other{# گھنٹے پہلے}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# دن پہلے}other{# دن پہلے}}"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 54b51d9..89e2e68 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Qo‘ng‘iroq qiluvchi ma’lumotlari cheklangan. Keyingi qo‘ng‘iroq: cheklanmagan"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Qo‘ng‘iroq qiluvchi ma’lumotlari cheklanmagan. Keyingi qo‘ng‘iroq: cheklangan"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Qo‘ng‘iroq qiluvchi ma’lumotlari cheklanmagan. Keyingi qo‘ng‘iroq: cheklanmagan"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Bu ilova 16 KB sahifa hajmi bilan mos emas. APK fayllardagi maʼlumotlarning tekislanishi tekshirilmadi. Bu ilova sahifa hajmiga mos rejimda ishlaydi. Eng yaxshi moslik uchun ilovani 16 KB sahifa hajmi bilan qayta kompilyatsiya qiling. Batafsil: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Bu ilova 16 KB sahifa hajmi bilan mos emas. ELF fayllardagi maʼlumotlarning tekislanishi tekshirilmadi. Bu ilova sahifa hajmiga mos rejimda ishlaydi. Eng yaxshi moslik uchun ilovani 16 KB sahifa hajmi bilan qayta kompilyatsiya qiling. Batafsil: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Bu ilova 16 KB sahifa hajmi bilan mos emas. APK va ELF fayllardagi maʼlumotlarning tekislanishi tekshirilmadi. Bu ilova sahifa hajmiga mos rejimda ishlaydi. Eng yaxshi moslik uchun ilovani 16 KB sahifa hajmi bilan qayta kompilyatsiya qiling. Batafsil: <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Xizmat ishalamaydi."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Qo‘ng‘iroq qiluvchining ID raqami sozlamasini o‘zgartirib bo‘lmaydi."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Internet <xliff:g id="CARRIERDISPLAY">%s</xliff:g> operatoriga almashtirildi"</string>
@@ -1161,6 +1158,22 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> soatdan keyin"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> kundan keyin"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> yildan keyin"</string>
+ <string name="duration_minutes_shortest_past" msgid="1740022450020492407">"<xliff:g id="COUNT">%d</xliff:g> daq oldin"</string>
+ <string name="duration_hours_shortest_past" msgid="2098397414186628489">"<xliff:g id="COUNT">%d</xliff:g> soat oldin"</string>
+ <string name="duration_days_shortest_past" msgid="1832006037955897625">"<xliff:g id="COUNT">%d</xliff:g> kun oldin"</string>
+ <string name="duration_years_shortest_past" msgid="6168256514200469291">"<xliff:g id="COUNT">%d</xliff:g> yil oldin"</string>
+ <string name="duration_minutes_medium" msgid="5891933490342643944">"<xliff:g id="COUNT">%d</xliff:g>daq"</string>
+ <string name="duration_hours_medium" msgid="1465359726485910115">"<xliff:g id="COUNT">%d</xliff:g>st"</string>
+ <string name="duration_days_medium" msgid="5994225628248661388">"<xliff:g id="COUNT">%d</xliff:g> k"</string>
+ <string name="duration_years_medium" msgid="734023884353592526">"<xliff:g id="COUNT">%d</xliff:g>yil"</string>
+ <string name="duration_minutes_medium_future" msgid="2750894988731934402">"<xliff:g id="COUNT">%d</xliff:g> daqiqadan keyin"</string>
+ <string name="duration_hours_medium_future" msgid="6050833881463849764">"<xliff:g id="COUNT">%d</xliff:g> soatdan keyin"</string>
+ <string name="duration_days_medium_future" msgid="1700821545602729963">"<xliff:g id="COUNT">%d</xliff:g> kundan keyin"</string>
+ <string name="duration_years_medium_future" msgid="3281018940397120166">"<xliff:g id="COUNT">%d</xliff:g> yildan keyin"</string>
+ <string name="duration_minutes_medium_past" msgid="7400424340181947714">"<xliff:g id="COUNT">%d</xliff:g> daqiqa oldin"</string>
+ <string name="duration_hours_medium_past" msgid="6709441336035202617">"<xliff:g id="COUNT">%d</xliff:g> soat oldin"</string>
+ <string name="duration_days_medium_past" msgid="5748156261134344532">"<xliff:g id="COUNT">%d</xliff:g> kun oldin"</string>
+ <string name="duration_years_medium_past" msgid="893797065424596243">"<xliff:g id="COUNT">%d</xliff:g> yil oldin"</string>
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# daqiqa oldin}other{# daqiqa oldin}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# soat oldin}other{# soat oldin}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# kun oldin}other{# kun oldin}}"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 9dc1a30..f42e46f 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Số gọi đến mặc định thành bị giới hạn. Cuộc gọi tiếp theo. Không bị giới hạn"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Số gọi đến mặc định thành không bị giới hạn. Cuộc gọi tiếp theo. Bị giới hạn"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Số gọi đến mặc định thành không bị giới hạn. Cuộc gọi tiếp theo. Không bị giới hạn"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Ứng dụng này không tương thích với kích thước trang 16 KB. Không kiểm tra được sự tương thích của tệp APK. Ứng dụng này sẽ chạy ở chế độ tương thích với kích thước trang. Để đảm bảo khả năng tương thích tốt nhất, vui lòng biên dịch lại ứng dụng ở chế độ hỗ trợ kích thước trang 16 KB. Để biết thêm thông tin, hãy xem <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Ứng dụng này không tương thích với kích thước trang 16 KB. Không kiểm tra được sự tương thích của tệp ELF. Ứng dụng này sẽ chạy ở chế độ tương thích với kích thước trang. Để đảm bảo khả năng tương thích tốt nhất, vui lòng biên dịch lại ứng dụng ở chế độ hỗ trợ kích thước trang 16 KB. Để biết thêm thông tin, hãy xem <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Ứng dụng này không tương thích với kích thước trang 16 KB. Không kiểm tra được sự tương thích của tệp APK và tệp ELF. Ứng dụng này sẽ chạy ở chế độ tương thích với kích thước trang. Để đảm bảo khả năng tương thích tốt nhất, vui lòng biên dịch lại ứng dụng ở chế độ hỗ trợ kích thước trang 16 KB. Để biết thêm thông tin, hãy xem <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Dịch vụ không được cấp phép."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Bạn không thể thay đổi cài đặt ID người gọi."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Đã chuyển dữ liệu sang <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> giờ nữa"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> ngày nữa"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> năm nữa"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# phút trước}other{# phút trước}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# giờ trước}other{# giờ trước}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# ngày trước}other{# ngày trước}}"</string>
diff --git a/core/res/res/values-watch/config_material.xml b/core/res/res/values-watch/config_material.xml
index 8e9693a..73a7c09 100644
--- a/core/res/res/values-watch/config_material.xml
+++ b/core/res/res/values-watch/config_material.xml
@@ -69,4 +69,14 @@
<integer name="config_motionExpressiveSlowSpatialStiffness">200</integer>
<item name="config_motionExpressiveSlowEffectDamping" format="float" type="dimen">1.0</item>
<integer name="config_motionExpressiveSlowEffectStiffness">260</integer>
+
+ <!--
+ Material rounded corner configs
+ Values from https://carbon.googleplex.com/wear-m3/tokens/designSystems/70fbaa4f7722a3d1/tokenSets/4fa2518eaeaf65eb
+ -->
+ <dimen name="config_shapeCornerRadiusXsmall">4dp</dimen>
+ <dimen name="config_shapeCornerRadiusSmall">8dp</dimen>
+ <dimen name="config_shapeCornerRadiusMedium">18dp</dimen>
+ <dimen name="config_shapeCornerRadiusLarge">26dp</dimen>
+ <dimen name="config_shapeCornerRadiusXlarge">36dp</dimen>
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 98f1898..14e8557 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"默认不显示本机号码,但在下一次通话中显示"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"默认显示本机号码,但在下一次通话中不显示"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"默认显示本机号码,在下一次通话中也显示"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"此应用不兼容 16 KB。APK 对齐检查失败。此应用将使用页面大小兼容模式运行。为获得最佳兼容性,请将此应用重新编译为支持 16 KB。如需了解详情,请参阅 <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"此应用不兼容 16 KB。ELF 对齐检查失败。此应用将使用页面大小兼容模式运行。为获得最佳兼容性,请将此应用重新编译为支持 16 KB。如需了解详情,请参阅 <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"此应用不兼容 16 KB。APK 和 ELF 对齐检查失败。此应用将使用页面大小兼容模式运行。为获得最佳兼容性,请将此应用重新编译为支持 16 KB。如需了解详情,请参阅 <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"未提供服务。"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"您无法更改来电显示设置。"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"移动数据网络已切换至“<xliff:g id="CARRIERDISPLAY">%s</xliff:g>”"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g>小时后"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g>天后"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g>年后"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# 分钟前}other{# 分钟前}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# 小时前}other{# 小时前}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# 天前}other{# 天前}}"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index a544b44..c4079e0 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"預設不顯示來電號碼,但下一通電話則顯示。"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"預設顯示來電號碼,但下一通電話不顯示。"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"預設顯示來電號碼,下一通電話也繼續顯示。"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"此應用程式不兼容 16KB。未能通過 APK 一致性檢查。此應用程式將使用頁面大小兼容模式運行。為達至最佳的兼容性,請將應用程式重新編譯為支援 16KB。詳情請瀏覽 <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"此應用程式不兼容 16KB。未能通過電子商務記錄格式一致性檢查。此應用程式將使用頁面大小兼容模式運行。為達至最佳的兼容性,請將應用程式重新編譯為支援 16KB。詳情請瀏覽 <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"此應用程式不兼容 16KB。未能通過 APK 和電子商務記錄格式一致性檢查。此應用程式將使用頁面大小兼容模式運行。為達至最佳的兼容性,請將應用程式重新編譯為支援 16KB。詳情請瀏覽 <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"未提供此服務。"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"你無法更改來電顯示設定。"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"流動數據已切換至「<xliff:g id="CARRIERDISPLAY">%s</xliff:g>」"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> 小時後"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> 天後"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> 年後"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# 分鐘前}other{# 分鐘前}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# 小時前}other{# 小時前}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# 天前}other{# 天前}}"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 68158ce..3c3a0ef 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"預設不顯示本機號碼,但下一通電話顯示。"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"預設顯示本機號碼,但下一通電話不顯示。"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"預設顯示本機號碼,下一通電話也繼續顯示。"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"這個應用程式不支援 16 KB,APK 校驗檢查失敗。這個應用程式將以頁面大小相容性模式執行。為獲得最佳相容性,請重新編譯,讓應用程式支援 16 KB。詳情請參閱 <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"這個應用程式不支援 16 KB,ELF 校驗檢查失敗。這個應用程式將以頁面大小相容性模式執行。為獲得最佳相容性,請重新編譯,讓應用程式支援 16 KB。詳情請參閱 <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"這個應用程式不支援 16 KB,APK 和 ELF 校驗檢查失敗。這個應用程式將以頁面大小相容性模式執行。為獲得最佳相容性,請重新編譯,讓應用程式支援 16 KB。詳情請參閱 <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"無法提供此服務。"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"你無法變更來電顯示設定。"</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"行動數據已切換至「<xliff:g id="CARRIERDISPLAY">%s</xliff:g>」"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"<xliff:g id="COUNT">%d</xliff:g> 小時後"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"<xliff:g id="COUNT">%d</xliff:g> 天後"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"<xliff:g id="COUNT">%d</xliff:g> 年後"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# 分鐘前}other{# 分鐘前}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# 小時前}other{# 小時前}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# 天前}other{# 天前}}"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 9047a98..dc72cb7 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -71,12 +71,9 @@
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"i-ID yomshayeli ishintshela kokuvinjiwe. Ucingo olulandelayo: Aluvinjelwe"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"I-ID Yomshayeli ishintshela kokungavinjelwe. Ucingo olulandelayo: Luvinjelwe"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"I-ID Yomshayeli ishintshela kokungavinjelwe. Ucingo olulandelayo: Aluvinjelwe"</string>
- <!-- no translation found for page_size_compat_apk_warning (2982396798449041224) -->
- <skip />
- <!-- no translation found for page_size_compat_elf_warning (6753874059564812651) -->
- <skip />
- <!-- no translation found for page_size_compat_apk_and_elf_warning (7628675779500605390) -->
- <skip />
+ <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Le app ayihambelani ne-16 KB. Ukuhlolwa kokuqondanisa i-APK kuhlulekile. Le app izoqaliswa kusetshenziswa imodi yokuhambelana kobukhulu bekhasi. Ukuze kube nokuhambelana okuhle kakhulu, sicela uhlanganise kabusha i-app ngosekelo lwe-16 KB. Ukuze uthole imininingwane eyengeziwe, bheka <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Le app ayihambelani ne-16 KB. Ukuhlolwa kokuqondanisa i-ELF kuhlulekile. Le app izoqaliswa kusetshenziswa imodi yokuhambelana kobukhulu bekhasi. Ukuze kube nokuhambelana okuhle kakhulu, sicela uhlanganise kabusha i-app ngosekelo lwe-16 KB. Ukuze uthole imininingwane eyengeziwe, bheka <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
+ <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Le app ayihambelani ne-16 KB. Ukuhlola ukuqondanisa i-APK ne-ELF kuhlulekile. Le app izoqaliswa kusetshenziswa imodi yokuhambelana kobukhulu bekhasi. Ukuze kube nokuhambelana okuhle kakhulu, sicela uhlanganise kabusha i-app ngosekelo lwe-16 KB. Ukuze uthole imininingwane eyengeziwe, bheka <a href=\"https://developer.android.com/16kb-page-size\">https://developer.android.com/16kb-page-size</a>"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Isevisi ayilungiselelwe."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Ngeke ukwazi ukuguqul izilungiselelo zemininingwane yoshayayo."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Ushintshele idatha ku-<xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -1161,6 +1158,38 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"ngehora elingu-<xliff:g id="COUNT">%d</xliff:g>"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"ngosuku olu-<xliff:g id="COUNT">%d</xliff:g>"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"ngonyaka ongu-<xliff:g id="COUNT">%d</xliff:g>"</string>
+ <!-- no translation found for duration_minutes_shortest_past (1740022450020492407) -->
+ <skip />
+ <!-- no translation found for duration_hours_shortest_past (2098397414186628489) -->
+ <skip />
+ <!-- no translation found for duration_days_shortest_past (1832006037955897625) -->
+ <skip />
+ <!-- no translation found for duration_years_shortest_past (6168256514200469291) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium (5891933490342643944) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium (1465359726485910115) -->
+ <skip />
+ <!-- no translation found for duration_days_medium (5994225628248661388) -->
+ <skip />
+ <!-- no translation found for duration_years_medium (734023884353592526) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_future (2750894988731934402) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_future (6050833881463849764) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_future (1700821545602729963) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_future (3281018940397120166) -->
+ <skip />
+ <!-- no translation found for duration_minutes_medium_past (7400424340181947714) -->
+ <skip />
+ <!-- no translation found for duration_hours_medium_past (6709441336035202617) -->
+ <skip />
+ <!-- no translation found for duration_days_medium_past (5748156261134344532) -->
+ <skip />
+ <!-- no translation found for duration_years_medium_past (893797065424596243) -->
+ <skip />
<string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{umzuzu odlule #}one{imizuzu edlule #}other{imizuzu edlule #}}"</string>
<string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{ihora elingu-# eledlule}one{amahora adlule angu-#}other{amahora adlule angu-#}}"</string>
<string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{usuku oludlule #}one{izinsuku ezedlule #}other{izinsuku ezedlule #}}"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 390fbea..53b47622 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2372,7 +2372,7 @@
<string name="default_sms_application" translatable="false">com.android.messaging</string>
<!-- Flag indicating whether the current device supports "Ask every time" for sms-->
- <bool name="config_sms_ask_every_time_support">true</bool>
+ <bool name="config_sms_ask_every_time_support">false</bool>
<!-- Flag indicating whether the current device allows acknowledgement of SIM operation like
SM-PP or saving SMS to SIM can be done via the IMS interfaces.
@@ -4228,13 +4228,12 @@
must match the value of config_cameraLaunchGestureSensorType in OEM's HAL -->
<string translatable="false" name="config_cameraLaunchGestureSensorStringType"></string>
- <!-- Allow the gesture to double tap the power button to trigger a target action. -->
- <bool name="config_doubleTapPowerGestureEnabled">true</bool>
<!-- Allow the gesture to double tap the power button twice to start the camera while the device
is non-interactive. -->
<bool name="config_cameraDoubleTapPowerGestureEnabled">true</bool>
- <!-- Allow the gesture to double tap the power button twice to launch the wallet. -->
- <bool name="config_walletDoubleTapPowerGestureEnabled">true</bool>
+
+ <!-- Allow the gesture to double tap the power button to trigger a target action. -->
+ <bool name="config_doubleTapPowerGestureEnabled">true</bool>
<!-- Default target action for double tap of the power button gesture.
0: Launch camera
1: Launch wallet -->
@@ -7276,4 +7275,9 @@
<!-- List of protected packages that require biometric authentication for modification
(Disable, force-stop or uninstalling updates). -->
<string-array name="config_biometric_protected_package_names" translatable="false" />
+
+ <!-- Package name of the on-device intelligent processor for vendor specific
+ features. Examples include the search functionality or the app
+ predictor. -->
+ <string name="config_systemVendorIntelligence" translatable="false"></string>
</resources>
diff --git a/core/res/res/values/config_material.xml b/core/res/res/values/config_material.xml
index 6034f9c..648fe90 100644
--- a/core/res/res/values/config_material.xml
+++ b/core/res/res/values/config_material.xml
@@ -75,4 +75,14 @@
<integer name="config_motionExpressiveSlowSpatialStiffness">200</integer>
<item name="config_motionExpressiveSlowEffectDamping" format="float" type="dimen">1.0</item>
<integer name="config_motionExpressiveSlowEffectStiffness">800</integer>
+
+ <!--
+ Material rounded corner configs
+ Values from https://carbon.googleplex.com/google-material-3/tokens/designSystems/20543ce18892f7d9/tokenSets/21c40db4e4f5af15
+ -->
+ <dimen name="config_shapeCornerRadiusXsmall">4dp</dimen>
+ <dimen name="config_shapeCornerRadiusSmall">8dp</dimen>
+ <dimen name="config_shapeCornerRadiusMedium">12dp</dimen>
+ <dimen name="config_shapeCornerRadiusLarge">16dp</dimen>
+ <dimen name="config_shapeCornerRadiusXlarge">28dp</dimen>
</resources>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index bb76b9f..196da29 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -496,4 +496,8 @@
not connected state. -->
<bool name="config_satellite_allow_check_message_in_not_connected">false</bool>
<java-symbol type="bool" name="config_satellite_allow_check_message_in_not_connected" />
+
+ <!-- Whether to allow TN scanning during satellite session. -->
+ <bool name="config_satellite_allow_tn_scanning_during_satellite_session">true</bool>
+ <java-symbol type="bool" name="config_satellite_allow_tn_scanning_during_satellite_session" />
</resources>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 90f1b8a..6c73b0c 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -154,6 +154,9 @@
<!-- @FlaggedApi(android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_PLATFORM_API_ENABLED)
@hide @SystemApi -->
<public name="config_defaultReservedForTestingProfileGroupExclusivity" />
+ <!-- @FlaggedApi(android.permission.flags.Flags.FLAG_SYSTEM_VENDOR_INTELLIGENCE_ROLE_ENABLED)
+ @hide @SystemApi -->
+ <public name="config_systemVendorIntelligence" />
</staging-public-group>
<staging-public-group type="dimen" first-id="0x01b30000">
@@ -181,6 +184,16 @@
<public name="config_motionExpressiveSlowSpatialDamping"/>
<!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_MOTION_TOKENS)-->
<public name="config_motionExpressiveSlowEffectDamping"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_SHAPE_TOKENS)-->
+ <public name="config_shapeCornerRadiusXsmall"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_SHAPE_TOKENS)-->
+ <public name="config_shapeCornerRadiusSmall"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_SHAPE_TOKENS)-->
+ <public name="config_shapeCornerRadiusMedium"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_SHAPE_TOKENS)-->
+ <public name="config_shapeCornerRadiusLarge"/>
+ <!-- @FlaggedApi(android.os.Flags.FLAG_MATERIAL_SHAPE_TOKENS)-->
+ <public name="config_shapeCornerRadiusXlarge"/>
</staging-public-group>
<staging-public-group type="color" first-id="0x01b20000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2671ff9..28de553 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3161,9 +3161,8 @@
<java-symbol type="string" name="config_cameraLaunchGestureSensorStringType" />
<java-symbol type="integer" name="config_cameraLiftTriggerSensorType" />
<java-symbol type="string" name="config_cameraLiftTriggerSensorStringType" />
- <java-symbol type="bool" name="config_doubleTapPowerGestureEnabled" />
<java-symbol type="bool" name="config_cameraDoubleTapPowerGestureEnabled" />
- <java-symbol type="bool" name="config_walletDoubleTapPowerGestureEnabled" />
+ <java-symbol type="bool" name="config_doubleTapPowerGestureEnabled" />
<java-symbol type="integer" name="config_defaultDoubleTapPowerGestureAction" />
<java-symbol type="bool" name="config_emergencyGestureEnabled" />
<java-symbol type="bool" name="config_defaultEmergencyGestureEnabled" />
@@ -5878,4 +5877,12 @@
<!-- List of protected packages that require biometric authentication for modification -->
<java-symbol type="array" name="config_biometric_protected_package_names" />
+ <!-- Material shape spec config tokens -->
+ <java-symbol type="dimen" name="config_shapeCornerRadiusXsmall"/>
+ <java-symbol type="dimen" name="config_shapeCornerRadiusSmall"/>
+ <java-symbol type="dimen" name="config_shapeCornerRadiusMedium"/>
+ <java-symbol type="dimen" name="config_shapeCornerRadiusLarge"/>
+ <java-symbol type="dimen" name="config_shapeCornerRadiusXlarge"/>
+
+
</resources>
diff --git a/core/res/res/xml/bookmarks.xml b/core/res/res/xml/bookmarks.xml
index e735784..17860ef 100644
--- a/core/res/res/xml/bookmarks.xml
+++ b/core/res/res/xml/bookmarks.xml
@@ -20,14 +20,10 @@
Typical shortcuts (not necessarily defined here):
'b': Browser
- 'c': Contacts
+ 'p': Contacts
'e': Email
- 'g': GMail
- 'k': Calendar
+ 'c': Calendar
'm': Maps
- 'p': Music
- 's': SMS
- 't': Talk
'u': Calculator
'y': YouTube
-->
@@ -38,7 +34,7 @@
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CONTACTS"
- androidprv:keycode="KEYCODE_C"
+ androidprv:keycode="KEYCODE_P"
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_EMAIL"
@@ -46,21 +42,13 @@
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CALENDAR"
- androidprv:keycode="KEYCODE_K"
+ androidprv:keycode="KEYCODE_C"
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_MAPS"
androidprv:keycode="KEYCODE_M"
androidprv:modifierState="META" />
<bookmark
- category="android.intent.category.APP_MUSIC"
- androidprv:keycode="KEYCODE_P"
- androidprv:modifierState="META" />
- <bookmark
- role="android.app.role.SMS"
- androidprv:keycode="KEYCODE_S"
- androidprv:modifierState="META" />
- <bookmark
category="android.intent.category.APP_CALCULATOR"
androidprv:keycode="KEYCODE_U"
androidprv:modifierState="META" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 4942557..c67a0f9 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -530,14 +530,25 @@
}
test_module_config {
- name: "FrameworksCoreTests_internal_os_binder",
+ name: "FrameworksCoreTests_all_binder",
base: "FrameworksCoreTests",
test_suites: [
"automotive-tests",
"device-tests",
"device-platinum-tests",
],
- include_filters: ["com.android.internal.os.BinderDeathDispatcherTest"],
+ include_filters: [
+ "android.os.BinderProxyTest",
+ "android.os.BinderDeathRecipientTest",
+ "android.os.BinderFrozenStateChangeNotificationTest",
+ "android.os.BinderProxyCountingTest",
+ "android.os.BinderUncaughtExceptionHandlerTest",
+ "android.os.BinderThreadPriorityTest",
+ "android.os.BinderWorkSourceTest",
+ "android.os.ParcelNullabilityTest",
+ "android.os.ParcelTest",
+ "com.android.internal.os.BinderDeathDispatcherTest",
+ ],
exclude_annotations: ["com.android.internal.os.SkipPresubmit"],
}
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 63e678d..9effeec 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -467,12 +467,25 @@
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.build();
assertThat(n.hasPromotableCharacteristics()).isTrue();
}
@Test
@EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+ public void testHasPromotableCharacteristics_notOngoing() {
+ Notification n = new Notification.Builder(mContext, "test")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
+ .setColor(Color.WHITE)
+ .setColorized(true)
+ .build();
+ assertThat(n.hasPromotableCharacteristics()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
public void testHasPromotableCharacteristics_wrongStyle() {
Notification n = new Notification.Builder(mContext, "test")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -480,6 +493,7 @@
.setContentTitle("TITLE")
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.build();
assertThat(n.hasPromotableCharacteristics()).isFalse();
}
@@ -491,6 +505,7 @@
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
+ .setOngoing(true)
.build();
assertThat(n.hasPromotableCharacteristics()).isFalse();
}
@@ -503,6 +518,7 @@
.setStyle(new Notification.BigTextStyle())
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.build();
assertThat(n.hasPromotableCharacteristics()).isFalse();
}
@@ -515,6 +531,7 @@
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.setGroup("someGroup")
.setGroupSummary(true)
.build();
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index 177c7f0..bd27337 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -740,5 +740,20 @@
assertEquals(null, cache.query(30));
// The recompute is 4 because nulls were not cached.
assertEquals(4, cache.getRecomputeCount());
+
+ // Verify that the default is not to cache nulls.
+ cache = new TestCache(new Args(MODULE_TEST)
+ .maxEntries(4).api("testCachingNulls"),
+ new TestQuery());
+ cache.invalidateCache();
+ assertEquals("foo1", cache.query(1));
+ assertEquals("foo2", cache.query(2));
+ assertEquals(null, cache.query(30));
+ assertEquals(3, cache.getRecomputeCount());
+ assertEquals("foo1", cache.query(1));
+ assertEquals("foo2", cache.query(2));
+ assertEquals(null, cache.query(30));
+ // The recompute is 4 because nulls were not cached.
+ assertEquals(4, cache.getRecomputeCount());
}
}
diff --git a/core/tests/coretests/src/android/app/QueuedWorkTest.java b/core/tests/coretests/src/android/app/QueuedWorkTest.java
index 230c9e8..6bd9b6a 100644
--- a/core/tests/coretests/src/android/app/QueuedWorkTest.java
+++ b/core/tests/coretests/src/android/app/QueuedWorkTest.java
@@ -163,18 +163,18 @@
@Test
public void testHasPendingWork() {
- Semaphore releaser = new Semaphore(0);
- mQueuedWork.queue(
- () -> {
- try {
- releaser.acquire();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }, false);
+ final Semaphore releaser1 = new Semaphore(0);
+ final Semaphore releaser2 = new Semaphore(0);
+ mQueuedWork.queue(() -> releaser1.acquireUninterruptibly(), false);
+ mQueuedWork.queue(() -> releaser2.release(), false);
+ // Worker should be waiting for releaser1,
+ // and have pending work to release releaser2
assertThat(mQueuedWork.hasPendingWork()).isTrue();
- releaser.release();
- mQueuedWork.waitToFinish();
+
+ // Allow worker to get to releasing releaser2
+ releaser1.release();
+ releaser2.acquireUninterruptibly();
+ // If we got here then there is no pending work.
assertThat(mQueuedWork.hasPendingWork()).isFalse();
}
}
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 8d045f8..1f1000f 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -49,6 +49,7 @@
import android.app.PictureInPictureParams;
import android.app.PictureInPictureUiState;
import android.app.ResourcesManager;
+import android.app.WindowConfiguration;
import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ActivityRelaunchItem;
import android.app.servertransaction.ClientTransaction;
@@ -79,6 +80,7 @@
import android.util.Log;
import android.util.MergedConfiguration;
import android.view.Display;
+import android.view.Surface;
import android.view.View;
import android.window.ActivityWindowInfo;
import android.window.WindowContextInfo;
@@ -302,6 +304,59 @@
assertScreenScale(originalScale, app, originalAppConfig, originalAppMetrics);
}
+ @Test
+ public void testOverrideDisplayRotation() throws Exception {
+ final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+ final Application app = activity.getApplication();
+ final ActivityThread activityThread = activity.getActivityThread();
+ final IApplicationThread appThread = activityThread.getApplicationThread();
+ final Configuration originalAppConfig =
+ new Configuration(app.getResources().getConfiguration());
+ final int originalDisplayRotation = originalAppConfig.windowConfiguration
+ .getDisplayRotation();
+
+ final Configuration newConfig = new Configuration(originalAppConfig);
+ newConfig.seq = BASE_SEQ + 1;
+
+ int sandboxedDisplayRotation = (originalDisplayRotation + 1) % 4;
+ CompatibilityInfo.setOverrideDisplayRotation(sandboxedDisplayRotation);
+ try {
+ // Send process level config change.
+ ClientTransaction transaction = newTransaction(activityThread);
+ transaction.addTransactionItem(
+ new ConfigurationChangeItem(newConfig, DEVICE_ID_INVALID));
+ appThread.scheduleTransaction(transaction);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertDisplayRotation(sandboxedDisplayRotation, app);
+
+ sandboxedDisplayRotation = (sandboxedDisplayRotation + 1) % 4;
+ CompatibilityInfo.setOverrideDisplayRotation(sandboxedDisplayRotation);
+ // Send activity level config change.
+ newConfig.seq++;
+ transaction = newTransaction(activityThread);
+ transaction.addTransactionItem(new ActivityConfigurationChangeItem(
+ activity.getActivityToken(), newConfig, new ActivityWindowInfo()));
+ appThread.scheduleTransaction(transaction);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertDisplayRotation(sandboxedDisplayRotation, activity);
+
+ // Execute a local relaunch item with current scaled config (e.g. simulate recreate),
+ // the config should not change again.
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> activityThread.executeTransaction(
+ newRelaunchResumeTransaction(activity)));
+
+ assertDisplayRotation(sandboxedDisplayRotation, activity);
+ } finally {
+ CompatibilityInfo.setOverrideDisplayRotation(WindowConfiguration.ROTATION_UNDEFINED);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> restoreConfig(activityThread, originalAppConfig));
+ }
+ assertDisplayRotation(originalDisplayRotation, app);
+ }
+
private static void assertScreenScale(float scale, Context context,
Configuration origConfig, DisplayMetrics origMetrics) {
final int expectedDpi = (int) (origConfig.densityDpi * scale + .5f);
@@ -326,6 +381,12 @@
assertEquals(expectedMaxBounds, currentConfig.windowConfiguration.getMaxBounds());
}
+ private static void assertDisplayRotation(@Surface.Rotation int expectedRotation,
+ Context context) {
+ final Configuration currentConfig = context.getResources().getConfiguration();
+ assertEquals(expectedRotation, currentConfig.windowConfiguration.getDisplayRotation());
+ }
+
@Test
public void testHandleActivityConfigurationChanged() {
final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
index 18e4fde..4a227d8 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
+++ b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
@@ -318,11 +318,11 @@
verifyDisplay(actualDisplay2, id = 2, width = 200f, height = 600f, POSITION_RIGHT,
offset = 0f, noOfChildren = 2)
- val actualDisplay3 = actualDisplay2.children[1]
+ val actualDisplay3 = actualDisplay2.children[0]
verifyDisplay(actualDisplay3, id = 3, width = 600f, height = 200f, POSITION_RIGHT,
offset = 10f, noOfChildren = 0)
- val actualDisplay4 = actualDisplay2.children[0]
+ val actualDisplay4 = actualDisplay2.children[1]
verifyDisplay(actualDisplay4, id = 4, width = 200f, height = 600f, POSITION_RIGHT,
offset = 210f, noOfChildren = 0)
}
@@ -402,42 +402,46 @@
@Test
fun rearrange_twoDisplays() {
- val nodes = rearrangeRects(
+ val root = rearrangeRects(
// Arrange in staggered manner, connected vertically.
RectF(100f, 100f, 250f, 200f),
RectF(150f, 200f, 300f, 300f),
)
- assertThat(nodes[0].children).containsExactly(nodes[1])
- assertThat(nodes[1].children).isEmpty()
- assertPositioning(nodes, Pair(POSITION_BOTTOM, 50f))
+ verifyDisplay(root, id = 0, width = 150f, height = 100f, noOfChildren = 1)
+ val node = root.children[0]
+ verifyDisplay(
+ node, id = 1, width = 150f, height = 100f, POSITION_BOTTOM, offset = 50f,
+ noOfChildren = 0)
}
@Test
fun rearrange_reverseOrderOfSeveralDisplays() {
- val nodes = rearrangeRects(
+ val root = rearrangeRects(
RectF(0f, 0f, 150f, 100f),
RectF(-150f, 0f, 0f, 100f),
RectF(-300f, 0f, -150f, 100f),
RectF(-450f, 0f, -300f, 100f),
)
- assertPositioning(
- nodes,
- Pair(POSITION_LEFT, 0f),
- Pair(POSITION_LEFT, 0f),
- Pair(POSITION_LEFT, 0f),
- )
-
- assertThat(nodes[0].children).containsExactly(nodes[1])
- assertThat(nodes[1].children).containsExactly(nodes[2])
- assertThat(nodes[2].children).containsExactly(nodes[3])
- assertThat(nodes[3].children).isEmpty()
+ verifyDisplay(root, id = 0, width = 150f, height = 100f, noOfChildren = 1)
+ var node = root.children[0]
+ verifyDisplay(
+ node, id = 1, width = 150f, height = 100f, POSITION_LEFT, offset = 0f,
+ noOfChildren = 1)
+ node = node.children[0]
+ verifyDisplay(
+ node, id = 2, width = 150f, height = 100f, POSITION_LEFT, offset = 0f,
+ noOfChildren = 1)
+ node = node.children[0]
+ verifyDisplay(
+ node, id = 3, width = 150f, height = 100f, POSITION_LEFT, offset = 0f,
+ noOfChildren = 0)
}
@Test
fun rearrange_crossWithRootInCenter() {
- val nodes = rearrangeRects(
+ val root = rearrangeRects(
RectF(0f, 0f, 150f, 100f),
RectF(-150f, 0f, 0f, 100f),
RectF(0f, -100f, 150f, 0f),
@@ -445,21 +449,24 @@
RectF(0f, 100f, 150f, 200f),
)
- assertPositioning(
- nodes,
- Pair(POSITION_LEFT, 0f),
- Pair(POSITION_TOP, 0f),
- Pair(POSITION_RIGHT, 0f),
- Pair(POSITION_BOTTOM, 0f),
- )
-
- assertThat(nodes[0].children)
- .containsExactly(nodes[1], nodes[2], nodes[3], nodes[4])
+ verifyDisplay(root, id = 0, width = 150f, height = 100f, noOfChildren = 4)
+ verifyDisplay(
+ root.children[0], id = 1, width = 150f, height = 100f, POSITION_LEFT, offset = 0f,
+ noOfChildren = 0)
+ verifyDisplay(
+ root.children[1], id = 2, width = 150f, height = 100f, POSITION_TOP, offset = 0f,
+ noOfChildren = 0)
+ verifyDisplay(
+ root.children[2], id = 3, width = 150f, height = 100f, POSITION_RIGHT, offset = 0f,
+ noOfChildren = 0)
+ verifyDisplay(
+ root.children[3], id = 4, width = 150f, height = 100f, POSITION_BOTTOM, offset = 0f,
+ noOfChildren = 0)
}
@Test
fun rearrange_elbowArrangementDoesNotUseCornerAdjacency1() {
- val nodes = rearrangeRects(
+ val root = rearrangeRects(
// 2
// |
// 0 - 1
@@ -469,20 +476,20 @@
RectF(100f, -100f, 200f, 0f),
)
- assertThat(nodes[0].children).containsExactly(nodes[1])
- assertThat(nodes[1].children).containsExactly(nodes[2])
- assertThat(nodes[2].children).isEmpty()
-
- assertPositioning(
- nodes,
- Pair(POSITION_RIGHT, 0f),
- Pair(POSITION_TOP, 0f),
- )
+ verifyDisplay(root, id = 0, width = 100f, height = 100f, noOfChildren = 1)
+ var node = root.children[0]
+ verifyDisplay(
+ node, id = 1, width = 100f, height = 100f, POSITION_RIGHT, offset = 0f,
+ noOfChildren = 1)
+ node = node.children[0]
+ verifyDisplay(
+ node, id = 2, width = 100f, height = 100f, POSITION_TOP,
+ offset = 0f, noOfChildren = 0)
}
@Test
fun rearrange_elbowArrangementDoesNotUseCornerAdjacency2() {
- val nodes = rearrangeRects(
+ val root = rearrangeRects(
// 0
// |
// 1
@@ -495,22 +502,24 @@
RectF(-100f, 200f, 0f, 300f),
)
- assertThat(nodes[0].children).containsExactly(nodes[1])
- assertThat(nodes[1].children).containsExactly(nodes[2])
- assertThat(nodes[2].children).containsExactly(nodes[3])
- assertThat(nodes[3].children).isEmpty()
-
- assertPositioning(
- nodes,
- Pair(POSITION_BOTTOM, 0f),
- Pair(POSITION_BOTTOM, 0f),
- Pair(POSITION_LEFT, 0f),
- )
+ verifyDisplay(root, id = 0, width = 100f, height = 100f, noOfChildren = 1)
+ var node = root.children[0]
+ verifyDisplay(
+ node, id = 1, width = 100f, height = 100f, POSITION_BOTTOM, offset = 0f,
+ noOfChildren = 1)
+ node = node.children[0]
+ verifyDisplay(
+ node, id = 2, width = 100f, height = 100f, POSITION_BOTTOM, offset = 0f,
+ noOfChildren = 1)
+ node = node.children[0]
+ verifyDisplay(
+ node, id = 3, width = 100f, height = 100f, POSITION_LEFT, offset = 0f,
+ noOfChildren = 0)
}
@Test
fun rearrange_useLargerEdge() {
- val nodes = rearrangeRects(
+ val root = rearrangeRects(
// 444111
// 444111
// 444111
@@ -527,23 +536,24 @@
RectF(0f, 0f, 30f, 30f),
)
- assertPositioning(
- nodes,
- Pair(POSITION_TOP, 10f),
- Pair(POSITION_RIGHT, 0f),
- Pair(POSITION_BOTTOM, -10f),
- Pair(POSITION_LEFT, 0f),
- )
-
- assertThat(nodes[0].children).containsExactly(nodes[1], nodes[2])
- assertThat(nodes[1].children).containsExactly(nodes[4])
- assertThat(nodes[2].children).containsExactly(nodes[3])
- (3..4).forEach { assertThat(nodes[it].children).isEmpty() }
+ verifyDisplay(root, id = 0, width = 30f, height = 30f, noOfChildren = 2)
+ verifyDisplay(
+ root.children[0], id = 1, width = 30f, height = 30f, POSITION_TOP,
+ offset = 10f, noOfChildren = 1)
+ verifyDisplay(
+ root.children[0].children[0], id = 4, width = 30f, height = 30f, POSITION_LEFT,
+ offset = 0f, noOfChildren = 0)
+ verifyDisplay(
+ root.children[1], id = 2, width = 30f, height = 30f, POSITION_RIGHT,
+ offset = 0f, noOfChildren = 1)
+ verifyDisplay(
+ root.children[1].children[0], id = 3, width = 30f, height = 30f, POSITION_BOTTOM,
+ offset = -10f, noOfChildren = 0)
}
@Test
fun rearrange_closeGaps() {
- val nodes = rearrangeRects(
+ val root = rearrangeRects(
// 000
// 000 111
// 000 111
@@ -558,16 +568,14 @@
// TOP/BOTTOM attach
)
- assertPositioning(
- nodes,
- // In the case of corner adjacency, we prefer a left/right attachment.
- Pair(POSITION_RIGHT, 10f),
- Pair(POSITION_BOTTOM, 30f),
- )
-
- assertThat(nodes[0].children).containsExactly(nodes[1])
- assertThat(nodes[1].children).containsExactly(nodes[2])
- assertThat(nodes[2].children).isEmpty()
+ verifyDisplay(root, id = 0, width = 30f, height = 30f, noOfChildren = 1)
+ verifyDisplay(
+ root.children[0], id = 1, width = 30f, height = 30f, POSITION_RIGHT, offset = 10f,
+ noOfChildren = 1)
+ // In the case of corner adjacency, we prefer a left/right attachment.
+ verifyDisplay(
+ root.children[0].children[0], id = 2, width = 29.5f, height = 30f, POSITION_BOTTOM,
+ offset = 30f, noOfChildren = 0)
}
@Test
@@ -612,8 +620,10 @@
/**
* Runs the rearrange algorithm and returns the resulting tree as a list of nodes, with the
* root at index 0. The number of nodes is inferred from the number of positions passed.
+ *
+ * Returns the root node.
*/
- private fun rearrangeRects(vararg pos: RectF): List<DisplayTopology.TreeNode> {
+ private fun rearrangeRects(vararg pos: RectF): DisplayTopology.TreeNode {
// Generates a linear sequence of nodes in order in the List from root to leaf,
// left-to-right. IDs are ascending from 0 to count - 1.
@@ -631,7 +641,7 @@
PointF(pos[it].left, pos[it].top)
})
- return nodes
+ return nodes[0]
}
private fun verifyDisplay(display: DisplayTopology.TreeNode, id: Int, width: Float,
@@ -644,11 +654,4 @@
assertThat(display.offset).isEqualTo(offset)
assertThat(display.children).hasSize(noOfChildren)
}
-
- private fun assertPositioning(
- nodes: List<DisplayTopology.TreeNode>, vararg positions: Pair<Int, Float>) {
- assertThat(nodes.drop(1).map { Pair(it.position, it.offset) })
- .containsExactly(*positions)
- .inOrder()
- }
}
diff --git a/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java b/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java
index 4172bff..9a679d8 100644
--- a/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java
+++ b/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java
@@ -31,6 +31,8 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.internal.os.SkipPresubmit;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -131,6 +133,7 @@
}
@Test
+ @SkipPresubmit("b/381950874: bitrot and failed")
public void testPassPriorityToService() throws Exception {
for (int prio = 19; prio >= -20; prio--) {
Process.setThreadPriority(prio);
@@ -146,6 +149,7 @@
}
@Test
+ @SkipPresubmit("b/381950874: bitrot and failed")
public void testCallBackFromServiceWithPriority() throws Exception {
for (int prio = -20; prio <= 19; prio++) {
final int expected = prio;
diff --git a/core/tests/coretests/src/android/os/IpcDataCacheTest.java b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
index e14608a..bb8356f 100644
--- a/core/tests/coretests/src/android/os/IpcDataCacheTest.java
+++ b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
@@ -16,13 +16,21 @@
package android.os;
+import static android.app.Flags.FLAG_PIC_CACHE_NULLS;
+import static android.app.Flags.FLAG_PIC_ISOLATE_CACHE_BY_UID;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
+import android.app.PropertyInvalidatedCache;
+import android.app.PropertyInvalidatedCache.Args;
import android.multiuser.Flags;
import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.ravenwood.RavenwoodRule;
+import android.os.IpcDataCache;
import androidx.test.filters.SmallTest;
@@ -43,6 +51,10 @@
@SmallTest
public class IpcDataCacheTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
// Configuration for creating caches
private static final String MODULE = IpcDataCache.MODULE_TEST;
private static final String API = "testApi";
@@ -287,7 +299,12 @@
@Override
public String apply(Integer qv) {
mRecomputeCount += 1;
- return "foo" + qv.toString();
+ // Special case for testing caches of nulls. Integers in the range 30-40 return null.
+ if (qv >= 30 && qv < 40) {
+ return null;
+ } else {
+ return "foo" + qv.toString();
+ }
}
int getRecomputeCount() {
@@ -406,31 +423,16 @@
}
@Test
- public void testConfig() {
+ public void testConfigDisable() {
+ // Create a set of caches based on a set of chained configs.
IpcDataCache.Config a = new IpcDataCache.Config(8, MODULE, "apiA");
TestCache ac = new TestCache(a);
- assertEquals(8, a.maxEntries());
- assertEquals(MODULE, a.module());
- assertEquals("apiA", a.api());
- assertEquals("apiA", a.name());
IpcDataCache.Config b = new IpcDataCache.Config(a, "apiB");
TestCache bc = new TestCache(b);
- assertEquals(8, b.maxEntries());
- assertEquals(MODULE, b.module());
- assertEquals("apiB", b.api());
- assertEquals("apiB", b.name());
IpcDataCache.Config c = new IpcDataCache.Config(a, "apiC", "nameC");
TestCache cc = new TestCache(c);
- assertEquals(8, c.maxEntries());
- assertEquals(MODULE, c.module());
- assertEquals("apiC", c.api());
- assertEquals("nameC", c.name());
IpcDataCache.Config d = a.child("nameD");
TestCache dc = new TestCache(d);
- assertEquals(8, d.maxEntries());
- assertEquals(MODULE, d.module());
- assertEquals("apiA", d.api());
- assertEquals("nameD", d.name());
a.disableForCurrentProcess();
assertEquals(ac.isDisabled(), true);
@@ -449,6 +451,7 @@
assertEquals(ec.isDisabled(), true);
}
+
// Verify that invalidating the cache from an app process would fail due to lack of permissions.
@Test
@android.platform.test.annotations.DisabledOnRavenwood(
@@ -507,4 +510,47 @@
// Re-enable test mode (so that the cleanup for the test does not throw).
IpcDataCache.setTestMode(true);
}
+
+ @RequiresFlagsEnabled(FLAG_PIC_CACHE_NULLS)
+ @Test
+ public void testCachingNulls() {
+ IpcDataCache.Config c =
+ new IpcDataCache.Config(4, IpcDataCache.MODULE_TEST, "testCachingNulls");
+ TestCache cache;
+ cache = new TestCache(c.cacheNulls(true));
+ cache.invalidateCache();
+ assertEquals("foo1", cache.query(1));
+ assertEquals("foo2", cache.query(2));
+ assertEquals(null, cache.query(30));
+ assertEquals(3, cache.getRecomputeCount());
+ assertEquals("foo1", cache.query(1));
+ assertEquals("foo2", cache.query(2));
+ assertEquals(null, cache.query(30));
+ assertEquals(3, cache.getRecomputeCount());
+
+ cache = new TestCache(c.cacheNulls(false));
+ cache.invalidateCache();
+ assertEquals("foo1", cache.query(1));
+ assertEquals("foo2", cache.query(2));
+ assertEquals(null, cache.query(30));
+ assertEquals(3, cache.getRecomputeCount());
+ assertEquals("foo1", cache.query(1));
+ assertEquals("foo2", cache.query(2));
+ assertEquals(null, cache.query(30));
+ // The recompute is 4 because nulls were not cached.
+ assertEquals(4, cache.getRecomputeCount());
+
+ // Verify that the default is not to cache nulls.
+ cache = new TestCache(c);
+ cache.invalidateCache();
+ assertEquals("foo1", cache.query(1));
+ assertEquals("foo2", cache.query(2));
+ assertEquals(null, cache.query(30));
+ assertEquals(3, cache.getRecomputeCount());
+ assertEquals("foo1", cache.query(1));
+ assertEquals("foo2", cache.query(2));
+ assertEquals(null, cache.query(30));
+ // The recompute is 4 because nulls were not cached.
+ assertEquals(4, cache.getRecomputeCount());
+ }
}
diff --git a/core/tests/coretests/src/android/os/TestLooperManagerTest.java b/core/tests/coretests/src/android/os/TestLooperManagerTest.java
deleted file mode 100644
index 4d64a3a..0000000
--- a/core/tests/coretests/src/android/os/TestLooperManagerTest.java
+++ /dev/null
@@ -1,91 +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.os;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-@RunWith(AndroidJUnit4.class)
-public class TestLooperManagerTest {
- private static final String TAG = "TestLooperManagerTest";
-
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
- .setProvideMainThread(true)
- .build();
-
- @Test
- public void testMainThread() throws Exception {
- doTest(Looper.getMainLooper());
- }
-
- @Test
- public void testCustomThread() throws Exception {
- final HandlerThread thread = new HandlerThread(TAG);
- thread.start();
- doTest(thread.getLooper());
- }
-
- private void doTest(Looper looper) throws Exception {
- final TestLooperManager tlm =
- InstrumentationRegistry.getInstrumentation().acquireLooperManager(looper);
-
- final Handler handler = new Handler(looper);
- final CountDownLatch latch = new CountDownLatch(1);
-
- assertFalse(tlm.hasMessages(handler, null, 42));
-
- handler.sendEmptyMessage(42);
- handler.post(() -> {
- latch.countDown();
- });
- assertTrue(tlm.hasMessages(handler, null, 42));
- assertFalse(latch.await(100, TimeUnit.MILLISECONDS));
-
- final Message first = tlm.next();
- assertEquals(42, first.what);
- assertNull(first.callback);
- tlm.execute(first);
- assertFalse(tlm.hasMessages(handler, null, 42));
- assertFalse(latch.await(100, TimeUnit.MILLISECONDS));
- tlm.recycle(first);
-
- final Message second = tlm.next();
- assertNotNull(second.callback);
- tlm.execute(second);
- assertFalse(tlm.hasMessages(handler, null, 42));
- assertTrue(latch.await(100, TimeUnit.MILLISECONDS));
- tlm.recycle(second);
-
- tlm.release();
- }
-}
diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java
index c3bd065..108f5ba 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java
@@ -211,6 +211,102 @@
}
@Test
+ public void testCalculateInsets_partialSideIntersection_leftCenter() {
+ mSource.setFrame(new Rect(0, 0, 100, 500));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(0, 100, 500, 400), false);
+ assertEquals(Insets.of(100, 0, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_leftTop() {
+ mSource.setFrame(new Rect(0, 0, 100, 500));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(0, -100, 500, 400), false);
+ assertEquals(Insets.of(100, 0, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_leftBottom() {
+ mSource.setFrame(new Rect(0, 0, 100, 500));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(0, 100, 500, 600), false);
+ assertEquals(Insets.of(100, 0, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_topCenter() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(-100, 0, 600, 500), false);
+ assertEquals(Insets.of(0, 100, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_topLeft() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(-100, 0, 400, 500), false);
+ assertEquals(Insets.of(0, 100, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_topRight() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(100, 0, 600, 500), false);
+ assertEquals(Insets.of(0, 100, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_rightCenter() {
+ mSource.setFrame(new Rect(400, 0, 500, 500));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(0, 100, 500, 400), false);
+ assertEquals(Insets.of(0, 0, 100, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_rightTop() {
+ mSource.setFrame(new Rect(400, 0, 500, 500));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(0, -100, 500, 400), false);
+ assertEquals(Insets.of(0, 0, 100, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_rightBottom() {
+ mSource.setFrame(new Rect(400, 0, 500, 500));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(0, 100, 500, 600), false);
+ assertEquals(Insets.of(0, 0, 100, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_bottomCenter() {
+ mSource.setFrame(new Rect(0, 400, 500, 500));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(-100, 0, 600, 500), false);
+ assertEquals(Insets.of(0, 0, 0, 100), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_bottomLeft() {
+ mSource.setFrame(new Rect(0, 400, 500, 500));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(-100, 0, 400, 500), false);
+ assertEquals(Insets.of(0, 0, 0, 100), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_partialSideIntersection_bottomRight() {
+ mSource.setFrame(new Rect(0, 400, 500, 500));
+ mSource.updateSideHint(new Rect(0, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(100, 0, 600, 500), false);
+ assertEquals(Insets.of(0, 0, 0, 100), insets);
+ }
+
+ @Test
public void testCalculateVisibleInsets_override() {
mSource.setFrame(new Rect(0, 0, 500, 100));
mSource.setVisibleFrame(new Rect(0, 0, 500, 200));
diff --git a/core/tests/coretests/src/android/view/ViewGroupTest.java b/core/tests/coretests/src/android/view/ViewGroupTest.java
index ae3ad36..43c404e 100644
--- a/core/tests/coretests/src/android/view/ViewGroupTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupTest.java
@@ -213,6 +213,35 @@
assertTrue(autofillableViews.containsAll(Arrays.asList(viewA, viewC)));
}
+ @Test
+ public void testMeasureCache() {
+ final int spec1 = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.AT_MOST);
+ final int spec2 = View.MeasureSpec.makeMeasureSpec(50, View.MeasureSpec.AT_MOST);
+ final Context context = getInstrumentation().getContext();
+ final View child = new View(context);
+ final TestView parent = new TestView(context, 0);
+ parent.addView(child);
+
+ child.setPadding(1, 2, 3, 4);
+ parent.measure(spec1, spec1);
+ assertEquals(4, parent.getMeasuredWidth());
+ assertEquals(6, parent.getMeasuredHeight());
+
+ child.setPadding(5, 6, 7, 8);
+ parent.measure(spec2, spec2);
+ assertEquals(12, parent.getMeasuredWidth());
+ assertEquals(14, parent.getMeasuredHeight());
+
+ // This ends the state of forceLayout.
+ parent.layout(0, 0, 50, 50);
+
+ // The cached values should be cleared after the new setPadding is called. And the measured
+ // width and height should be up-to-date.
+ parent.measure(spec1, spec1);
+ assertEquals(12, parent.getMeasuredWidth());
+ assertEquals(14, parent.getMeasuredHeight());
+ }
+
private static void getUnobscuredTouchableRegion(Region outRegion, View view) {
outRegion.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
final ViewParent parent = view.getParent();
@@ -240,6 +269,19 @@
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// We don't layout this view.
}
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int measuredWidth = 0;
+ int measuredHeight = 0;
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ measuredWidth += child.getPaddingLeft() + child.getPaddingRight();
+ measuredHeight += child.getPaddingTop() + child.getPaddingBottom();
+ }
+ setMeasuredDimension(measuredWidth, measuredHeight);
+ }
}
public static class AutofillableTestView extends TestView {
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 402b92a..26806b1 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -16,16 +16,23 @@
package android.view.textclassifier;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.mock;
+import android.Manifest;
import android.content.Context;
+import android.permission.flags.Flags;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -61,4 +68,28 @@
assertThat(mTcm.getTextClassifier(TextClassifier.SYSTEM))
.isInstanceOf(SystemTextClassifier.class);
}
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_TEXT_CLASSIFIER_CHOICE_API_ENABLED)
+ public void testGetClassifier() {
+ Assume.assumeTrue(Flags.textClassifierChoiceApiEnabled());
+ assertThrows(SecurityException.class,
+ () -> mTcm.getClassifier(TextClassifier.CLASSIFIER_TYPE_DEVICE_DEFAULT));
+ assertThrows(SecurityException.class,
+ () -> mTcm.getClassifier(TextClassifier.CLASSIFIER_TYPE_ANDROID_DEFAULT));
+ assertThrows(SecurityException.class,
+ () -> mTcm.getClassifier(TextClassifier.CLASSIFIER_TYPE_SELF_PROVIDED));
+
+ runWithShellPermissionIdentity(() -> {
+ assertThat(
+ mTcm.getClassifier(TextClassifier.CLASSIFIER_TYPE_DEVICE_DEFAULT)).isInstanceOf(
+ SystemTextClassifier.class);
+ assertThat(mTcm.getClassifier(
+ TextClassifier.CLASSIFIER_TYPE_ANDROID_DEFAULT)).isInstanceOf(
+ SystemTextClassifier.class);
+ assertThat(mTcm.getClassifier(
+ TextClassifier.CLASSIFIER_TYPE_SELF_PROVIDED)).isSameInstanceAs(
+ TextClassifier.NO_OP);
+ }, Manifest.permission.ACCESS_TEXT_CLASSIFIER_BY_TYPE);
+ }
}
diff --git a/core/tests/coretests/src/android/window/WindowContextTest.java b/core/tests/coretests/src/android/window/WindowContextTest.java
index 21930d1..b934426 100644
--- a/core/tests/coretests/src/android/window/WindowContextTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextTest.java
@@ -33,7 +33,9 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.Activity;
import android.app.EmptyActivity;
@@ -48,7 +50,9 @@
import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.IBinder;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.view.Display;
import android.view.IWindowManager;
import android.view.View;
@@ -64,6 +68,7 @@
import androidx.test.rule.ActivityTestRule;
import com.android.frameworks.coretests.R;
+import com.android.window.flags.Flags;
import org.junit.After;
import org.junit.Before;
@@ -91,6 +96,8 @@
public ActivityTestRule<EmptyActivity> mActivityRule =
new ActivityTestRule<>(EmptyActivity.class, false /* initialTouchMode */,
false /* launchActivity */);
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
private final WindowContext mWindowContext = createWindowContext();
@@ -340,17 +347,35 @@
}
@Test
- public void updateDisplay_wasAttached_detachThenAttachedPropagatedToTokenController() {
+ @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
+ public void reparentToDisplayId_wasAttached_reparentToDisplayAreaPropagatedToTokenController() {
final WindowTokenClientController mockWindowTokenClientController =
mock(WindowTokenClientController.class);
+ when(mockWindowTokenClientController.attachToDisplayArea(any(), anyInt(), anyInt(),
+ any())).thenReturn(true);
WindowTokenClientController.overrideForTesting(mockWindowTokenClientController);
- mWindowContext.updateDisplay(DEFAULT_DISPLAY + 1);
+ mWindowContext.reparentToDisplay(DEFAULT_DISPLAY + 1);
- verify(mockWindowTokenClientController).detachIfNeeded(any());
- verify(mockWindowTokenClientController).attachToDisplayArea(any(),
- anyInt(), /* displayId= */ eq(DEFAULT_DISPLAY + 1),
- any());
+ verify(mockWindowTokenClientController).reparentToDisplayArea(any(),
+ /* displayId= */ eq(DEFAULT_DISPLAY + 1)
+ );
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
+ public void reparentToDisplayId_sameDisplayId_noReparenting() {
+ final WindowTokenClientController mockWindowTokenClientController =
+ mock(WindowTokenClientController.class);
+ when(mockWindowTokenClientController.attachToDisplayArea(any(), anyInt(), anyInt(),
+ any())).thenReturn(true);
+ WindowTokenClientController.overrideForTesting(mockWindowTokenClientController);
+
+ mWindowContext.reparentToDisplay(DEFAULT_DISPLAY);
+
+ verify(mockWindowTokenClientController, never()).reparentToDisplayArea(any(),
+ /* displayId= */ eq(DEFAULT_DISPLAY)
+ );
}
private WindowContext createWindowContext() {
diff --git a/core/tests/coretests/src/com/android/internal/view/ScrollCaptureInternalTest.java b/core/tests/coretests/src/com/android/internal/view/ScrollCaptureInternalTest.java
new file mode 100644
index 0000000..5f6d806
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/view/ScrollCaptureInternalTest.java
@@ -0,0 +1,243 @@
+/*
+ * 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.internal.view;
+
+import static android.view.flags.Flags.FLAG_SCROLL_CAPTURE_RELAX_SCROLL_VIEW_CRITERIA;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.internal.view.ScrollCaptureInternal.TYPE_FIXED;
+import static com.android.internal.view.ScrollCaptureInternal.TYPE_OPAQUE;
+import static com.android.internal.view.ScrollCaptureInternal.TYPE_RECYCLING;
+import static com.android.internal.view.ScrollCaptureInternal.TYPE_SCROLLING;
+import static com.android.internal.view.ScrollCaptureInternal.detectScrollingType;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.testing.AndroidTestingRunner;
+import android.view.ViewGroup;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests scrolling detection.
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ScrollCaptureInternalTest {
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ /**
+ * Tests the effect of padding on scroll capture search dispatch.
+ * <p>
+ * Verifies computation of child visible bounds with padding.
+ */
+ @Test
+ public void testDetectScrollingType_scrolling_notScrollable() {
+ MockScrollable scrollable = new MockScrollable.Builder()
+ .bounds(0, 0, 200, 200)
+ .childCount(1)
+ .canScrollUp(false)
+ .canScrollDown(false)
+ .scrollToEnabled(false)
+ .build(getInstrumentation().getContext());
+
+ assertEquals(TYPE_FIXED, detectScrollingType(scrollable));
+ }
+
+ @Test
+ public void testDetectScrollingType_scrolling_noChildren() {
+ MockScrollable scrollable = new MockScrollable.Builder()
+ .bounds(0, 0, 200, 200)
+ .childCount(0)
+ .canScrollUp(false)
+ .canScrollDown(true)
+ .scrollToEnabled(true)
+ .build(getInstrumentation().getContext());
+
+ assertEquals(TYPE_OPAQUE, detectScrollingType(scrollable));
+ }
+
+ @Test
+ public void testDetectScrollingType_scrolling() {
+ MockScrollable scrollable = new MockScrollable.Builder()
+ .bounds(0, 0, 200, 200)
+ .childCount(1)
+ .canScrollUp(false)
+ .canScrollDown(true)
+ .scrollToEnabled(true)
+ .build(getInstrumentation().getContext());
+
+ assertEquals(TYPE_SCROLLING, detectScrollingType(scrollable));
+ }
+
+ @Test
+ public void testDetectScrollingType_scrolling_partiallyScrolled() {
+ MockScrollable scrollable = new MockScrollable.Builder()
+ .bounds(0, 0, 200, 200)
+ .childCount(1)
+ .canScrollUp(true)
+ .canScrollDown(true)
+ .scrollToEnabled(true)
+ .build(getInstrumentation().getContext());
+ scrollable.scrollTo(0, 100);
+
+ assertEquals(TYPE_SCROLLING, detectScrollingType(scrollable));
+ }
+
+ @Test
+ @EnableFlags(FLAG_SCROLL_CAPTURE_RELAX_SCROLL_VIEW_CRITERIA)
+ public void testDetectScrollingType_scrolling_multipleChildren() {
+ MockScrollable scrollable = new MockScrollable.Builder()
+ .bounds(0, 0, 200, 200)
+ .childCount(10)
+ .canScrollUp(false)
+ .canScrollDown(true)
+ .scrollToEnabled(true)
+ .build(getInstrumentation().getContext());
+
+ assertEquals(TYPE_SCROLLING, detectScrollingType(scrollable));
+ }
+
+ @Test
+ public void testDetectScrollingType_recycling() {
+ MockScrollable scrollable = new MockScrollable.Builder()
+ .bounds(0, 0, 200, 200)
+ .childCount(10)
+ .canScrollUp(false)
+ .canScrollDown(true)
+ .scrollToEnabled(false)
+ .build(getInstrumentation().getContext());
+
+ assertEquals(TYPE_RECYCLING, detectScrollingType(scrollable));
+ }
+
+ @Test
+ public void testDetectScrollingType_noChildren() {
+ MockScrollable scrollable = new MockScrollable.Builder()
+ .bounds(0, 0, 200, 200)
+ .childCount(0)
+ .canScrollUp(true)
+ .canScrollDown(true)
+ .scrollToEnabled(false)
+ .build(getInstrumentation().getContext());
+
+ assertEquals(TYPE_OPAQUE, detectScrollingType(scrollable));
+ }
+
+
+ /**
+ * A mock which can exhibit some attributes and behaviors used to detect different types
+ * of scrolling content.
+ */
+ private static class MockScrollable extends ViewGroup {
+ private final int mChildCount;
+ private final boolean mCanScrollUp;
+ private final boolean mCanScrollDown;
+ private final boolean mScrollToEnabled;
+
+ MockScrollable(Context context, Rect bounds, int childCount, boolean canScrollUp,
+ boolean canScrollDown, boolean scrollToEnabled) {
+ super(context);
+ setFrame(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ mCanScrollUp = canScrollUp;
+ mCanScrollDown = canScrollDown;
+ mScrollToEnabled = scrollToEnabled;
+ mChildCount = childCount;
+ }
+
+ private static class Builder {
+ private int mChildCount;
+ private boolean mCanScrollUp;
+ private boolean mCanScrollDown;
+ private boolean mScrollToEnabled = true;
+
+ private final Rect mBounds = new Rect();
+
+ public MockScrollable build(Context context) {
+ return new MockScrollable(context,
+ mBounds, mChildCount, mCanScrollUp, mCanScrollDown,
+ mScrollToEnabled);
+ }
+
+ public Builder canScrollUp(boolean canScrollUp) {
+ mCanScrollUp = canScrollUp;
+ return this;
+ }
+
+ public Builder canScrollDown(boolean canScrollDown) {
+ mCanScrollDown = canScrollDown;
+ return this;
+ }
+
+ public Builder scrollToEnabled(boolean enabled) {
+ mScrollToEnabled = enabled;
+ return this;
+ }
+
+ public Builder childCount(int childCount) {
+ mChildCount = childCount;
+ return this;
+ }
+
+ public Builder bounds(int left, int top, int right, int bottom) {
+ mBounds.set(left, top, right, bottom);
+ return this;
+ }
+ }
+
+ @Override
+ public boolean canScrollVertically(int direction) {
+ if (direction > 0) {
+ return mCanScrollDown;
+ } else if (direction < 0) {
+ return mCanScrollUp;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int getChildCount() {
+ return mChildCount;
+ }
+
+ @Override
+ public void scrollTo(int x, int y) {
+ if (mScrollToEnabled) {
+ super.scrollTo(x, y);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ // We don't layout this view.
+ }
+ }
+}
diff --git a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
index 3b9f35b..e6586b3 100644
--- a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
@@ -496,4 +496,58 @@
// expected
}
}
+
+ // Note: the zeroize() tests only test the behavior that can be tested from a Java test.
+ // They do not verify that no copy of the data is left anywhere.
+
+ @Test
+ @SmallTest
+ public void testZeroizeNonMovableByteArray() {
+ final int length = 10;
+ byte[] array = ArrayUtils.newNonMovableByteArray(length);
+ assertArrayEquals(array, new byte[length]);
+ Arrays.fill(array, (byte) 0xff);
+ ArrayUtils.zeroize(array);
+ assertArrayEquals(array, new byte[length]);
+ }
+
+ @Test
+ @SmallTest
+ public void testZeroizeRegularByteArray() {
+ final int length = 10;
+ byte[] array = new byte[length];
+ assertArrayEquals(array, new byte[length]);
+ Arrays.fill(array, (byte) 0xff);
+ ArrayUtils.zeroize(array);
+ assertArrayEquals(array, new byte[length]);
+ }
+
+ @Test
+ @SmallTest
+ public void testZeroizeNonMovableCharArray() {
+ final int length = 10;
+ char[] array = ArrayUtils.newNonMovableCharArray(length);
+ assertArrayEquals(array, new char[length]);
+ Arrays.fill(array, (char) 0xff);
+ ArrayUtils.zeroize(array);
+ assertArrayEquals(array, new char[length]);
+ }
+
+ @Test
+ @SmallTest
+ public void testZeroizeRegularCharArray() {
+ final int length = 10;
+ char[] array = new char[length];
+ assertArrayEquals(array, new char[length]);
+ Arrays.fill(array, (char) 0xff);
+ ArrayUtils.zeroize(array);
+ assertArrayEquals(array, new char[length]);
+ }
+
+ @Test
+ @SmallTest
+ public void testZeroize_acceptsNull() {
+ ArrayUtils.zeroize((byte[]) null);
+ ArrayUtils.zeroize((char[]) null);
+ }
}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 9bf4d65..2e88514 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -34,6 +34,7 @@
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
+import android.graphics.fonts.FontStyle;
import android.graphics.fonts.FontVariationAxis;
import android.graphics.text.TextRunShaper;
import android.os.Build;
@@ -2141,6 +2142,14 @@
* @see FontVariationAxis
*/
public boolean setFontVariationSettings(String fontVariationSettings) {
+ return setFontVariationSettings(fontVariationSettings, 0 /* wght adjust */);
+ }
+
+ /**
+ * Set font variation settings with weight adjustment
+ * @hide
+ */
+ public boolean setFontVariationSettings(String fontVariationSettings, int wghtAdjust) {
final boolean useFontVariationStore = Flags.typefaceRedesignReadonly()
&& CompatChanges.isChangeEnabled(NEW_FONT_VARIATION_MANAGEMENT);
if (useFontVariationStore) {
@@ -2154,8 +2163,13 @@
long builderPtr = nCreateFontVariationBuilder(axes.length);
for (int i = 0; i < axes.length; ++i) {
- nAddFontVariationToBuilder(builderPtr, axes[i].getOpenTypeTagValue(),
- axes[i].getStyleValue());
+ int tag = axes[i].getOpenTypeTagValue();
+ float value = axes[i].getStyleValue();
+ if (tag == 0x77676874 /* wght */) {
+ value = Math.clamp(value + wghtAdjust,
+ FontStyle.FONT_WEIGHT_MIN, FontStyle.FONT_WEIGHT_MAX);
+ }
+ nAddFontVariationToBuilder(builderPtr, tag, value);
}
nSetFontVariationOverride(mNativePaint, builderPtr);
mFontVariationSettings = fontVariationSettings;
diff --git a/keystore/java/Android.bp b/keystore/java/Android.bp
index 21edff1..264ac5ff 100644
--- a/keystore/java/Android.bp
+++ b/keystore/java/Android.bp
@@ -13,5 +13,13 @@
"**/*.java",
"**/*.aidl",
],
+ exclude_srcs: select(release_flag("RELEASE_ATTEST_MODULES"), {
+ true: [
+ "android/security/KeyStore2HalCurrent.java",
+ ],
+ default: [
+ "android/security/KeyStore2HalLatest.java",
+ ],
+ }),
visibility: ["//frameworks/base"],
}
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index dd703f5..f5cf571 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -101,7 +101,7 @@
R execute(IKeystoreService service) throws RemoteException;
}
- private <R> R handleRemoteExceptionWithRetry(@NonNull CheckedRemoteRequest<R> request)
+ <R> R handleRemoteExceptionWithRetry(@NonNull CheckedRemoteRequest<R> request)
throws KeyStoreException {
IKeystoreService service = getService(false /* retryLookup */);
boolean firstTry = true;
@@ -369,6 +369,18 @@
}
}
+ /**
+ * Returns tag-specific info required to interpret a tag's attested value.
+ * @see IKeystoreService#getSupplementaryAttestationInfo(Tag) for more details.
+ * @param tag
+ * @return
+ * @throws KeyStoreException
+ * @hide
+ */
+ public byte[] getSupplementaryAttestationInfo(int tag) throws KeyStoreException {
+ return KeyStore2HalVersion.getSupplementaryAttestationInfoHelper(tag, this);
+ }
+
static KeyStoreException getKeyStoreException(int errorCode, String serviceErrorMessage) {
if (errorCode > 0) {
// KeyStore layer error
diff --git a/keystore/java/android/security/KeyStore2HalCurrent.java b/keystore/java/android/security/KeyStore2HalCurrent.java
new file mode 100644
index 0000000..f4d8fe6
--- /dev/null
+++ b/keystore/java/android/security/KeyStore2HalCurrent.java
@@ -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 android.security;
+
+/**
+ * @hide This class is necessary to allow the version of the AIDL interface for Keystore and
+* KeyMint used in KeyStore2.java to differ by BUILD flag `RELEASE_ATTEST_MODULES`. When
+* `RELEASE_ATTEST_MODULES` is not set, this file is included, and the current HALs for Keystore
+* (V4) and KeyMint (V3) are used.
+*/
+class KeyStore2HalVersion {
+ public static byte[] getSupplementaryAttestationInfoHelper(int tag, KeyStore2 ks)
+ throws KeyStoreException {
+ return new byte[0];
+ }
+}
diff --git a/keystore/java/android/security/KeyStore2HalLatest.java b/keystore/java/android/security/KeyStore2HalLatest.java
new file mode 100644
index 0000000..123f1c0
--- /dev/null
+++ b/keystore/java/android/security/KeyStore2HalLatest.java
@@ -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 android.security;
+
+/**
+ * @hide This class is necessary to allow the version of the AIDL interface for Keystore and
+* KeyMint used in KeyStore2.java to differ by BUILD flag `RELEASE_ATTEST_MODULES`. When
+* `RELEASE_ATTEST_MODULES` is set, this file is included, and the latest HALs for Keystore (V5)
+* and KeyMint (V4) are used.
+*/
+class KeyStore2HalVersion {
+ public static byte[] getSupplementaryAttestationInfoHelper(int tag, KeyStore2 ks)
+ throws KeyStoreException {
+ return ks.handleRemoteExceptionWithRetry(
+ (service) -> service.getSupplementaryAttestationInfo(tag));
+ }
+}
diff --git a/keystore/java/android/security/keystore/KeyStoreManager.java b/keystore/java/android/security/keystore/KeyStoreManager.java
index e6091c1..740ccb5 100644
--- a/keystore/java/android/security/keystore/KeyStoreManager.java
+++ b/keystore/java/android/security/keystore/KeyStoreManager.java
@@ -17,9 +17,11 @@
package android.security.keystore;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemService;
import android.content.Context;
+import android.hardware.security.keymint.TagType;
import android.security.KeyStore2;
import android.security.KeyStoreException;
import android.security.keystore2.AndroidKeyStoreProvider;
@@ -32,6 +34,8 @@
import com.android.internal.annotations.GuardedBy;
import java.io.ByteArrayInputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.security.Key;
import java.security.KeyPair;
import java.security.PublicKey;
@@ -299,6 +303,37 @@
return Collections.emptyList();
}
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {MODULE_HASH})
+ public @interface SupplementaryAttestationInfoTagEnum {}
+
+ /**
+ * When passed into getSupplementaryAttestationInfo, getSupplementaryAttestationInfo returns the
+ * DER-encoded structure corresponding to the `Modules` schema described in the KeyMint HAL's
+ * KeyCreationResult.aidl. The SHA-256 hash of this encoded structure is what's included with
+ * the tag in attestations.
+ */
+ // TODO(b/369375199): Replace with Tag.MODULE_HASH when flagging is removed.
+ public static final int MODULE_HASH = TagType.BYTES | 724;
+
+ /**
+ * Returns tag-specific data required to interpret a tag's attested value.
+ *
+ * When performing key attestation, the obtained attestation certificate contains a list of tags
+ * and their corresponding attested values. For some tags, additional information about the
+ * attested value can be queried via this API. See individual tags for specifics.
+ *
+ * @param tag tag for which info is being requested
+ * @return tag-specific info
+ * @throws KeyStoreException if the requested info is not available
+ */
+ @FlaggedApi(android.security.keystore2.Flags.FLAG_ATTEST_MODULES)
+ public @NonNull byte[] getSupplementaryAttestationInfo(
+ @SupplementaryAttestationInfoTagEnum int tag) throws KeyStoreException {
+ return mKeyStore2.getSupplementaryAttestationInfo(tag);
+ }
+
/**
* Returns a new {@link KeyDescriptor} instance in the app domain / namespace with the {@code
* alias} set to the provided value.
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index 9ea2943..f0613ce 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -398,27 +398,23 @@
new TaskFragmentAnimationParams.Builder();
final int animationBackgroundColor = getAnimationBackgroundColor(splitAttributes);
builder.setAnimationBackgroundColor(animationBackgroundColor);
- if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- final int openAnimationResId =
- splitAttributes.getAnimationParams().getOpenAnimationResId();
- builder.setOpenAnimationResId(openAnimationResId);
- final int closeAnimationResId =
- splitAttributes.getAnimationParams().getCloseAnimationResId();
- builder.setCloseAnimationResId(closeAnimationResId);
- final int changeAnimationResId =
- splitAttributes.getAnimationParams().getChangeAnimationResId();
- builder.setChangeAnimationResId(changeAnimationResId);
- }
+ final int openAnimationResId =
+ splitAttributes.getAnimationParams().getOpenAnimationResId();
+ builder.setOpenAnimationResId(openAnimationResId);
+ final int closeAnimationResId =
+ splitAttributes.getAnimationParams().getCloseAnimationResId();
+ builder.setCloseAnimationResId(closeAnimationResId);
+ final int changeAnimationResId =
+ splitAttributes.getAnimationParams().getChangeAnimationResId();
+ builder.setChangeAnimationResId(changeAnimationResId);
return builder.build();
}
@ColorInt
private static int getAnimationBackgroundColor(@NonNull SplitAttributes splitAttributes) {
int animationBackgroundColor = DEFAULT_ANIMATION_BACKGROUND_COLOR;
- AnimationBackground animationBackground = splitAttributes.getAnimationBackground();
- if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- animationBackground = splitAttributes.getAnimationParams().getAnimationBackground();
- }
+ final AnimationBackground animationBackground =
+ splitAttributes.getAnimationParams().getAnimationBackground();
if (animationBackground instanceof AnimationBackground.ColorBackground colorBackground) {
animationBackgroundColor = colorBackground.getColor();
}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/TestShellExecutor.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/TestShellExecutor.kt
new file mode 100644
index 0000000..ef8e71c
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/TestShellExecutor.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.wm.shell
+
+import com.android.wm.shell.common.ShellExecutor
+
+/**
+ * Simple implementation of [ShellExecutor] that collects all runnables and executes them
+ * sequentially once [flushAll] is called
+ */
+class TestShellExecutor : ShellExecutor {
+
+ private val runnables: MutableList<Runnable> = mutableListOf()
+
+ override fun execute(runnable: Runnable) {
+ runnables.add(runnable)
+ }
+
+ override fun executeDelayed(runnable: Runnable, delayMillis: Long) {
+ execute(runnable)
+ }
+
+ override fun removeCallbacks(runnable: Runnable?) {}
+
+ override fun hasCallback(runnable: Runnable?): Boolean = false
+
+ /**
+ * Execute all posted runnables sequentially
+ */
+ fun flushAll() {
+ while (runnables.isNotEmpty()) {
+ runnables.removeAt(0).run()
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt
index f535fbd..2b4e541 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt
@@ -34,6 +34,7 @@
import com.android.internal.statusbar.IStatusBarService
import com.android.wm.shell.Flags
import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.WindowManagerShellWrapper
import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
import com.android.wm.shell.bubbles.properties.ProdBubbleProperties
@@ -41,7 +42,6 @@
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayInsetsController
import com.android.wm.shell.common.FloatingContentCoordinator
-import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.common.TaskStackListenerImpl
import com.android.wm.shell.draganddrop.DragAndDropController
@@ -84,16 +84,16 @@
private lateinit var uiEventLoggerFake: UiEventLoggerFake
private lateinit var bubblePositioner: BubblePositioner
private lateinit var bubbleData: BubbleData
- private lateinit var mainExecutor: TestExecutor
- private lateinit var bgExecutor: TestExecutor
+ private lateinit var mainExecutor: TestShellExecutor
+ private lateinit var bgExecutor: TestShellExecutor
@Before
fun setUp() {
ProtoLog.REQUIRE_PROTOLOGTOOL = false
ProtoLog.init()
- mainExecutor = TestExecutor()
- bgExecutor = TestExecutor()
+ mainExecutor = TestShellExecutor()
+ bgExecutor = TestShellExecutor()
uiEventLoggerFake = UiEventLoggerFake()
val bubbleLogger = BubbleLogger(uiEventLoggerFake)
@@ -232,8 +232,8 @@
bubbleData: BubbleData,
bubbleLogger: BubbleLogger,
bubblePositioner: BubblePositioner,
- mainExecutor: TestExecutor,
- bgExecutor: TestExecutor,
+ mainExecutor: TestShellExecutor,
+ bgExecutor: TestShellExecutor,
): BubbleController {
val shellCommandHandler = ShellCommandHandler()
val shellController =
@@ -289,29 +289,6 @@
)
}
- private class TestExecutor : ShellExecutor {
-
- private val runnables: MutableList<Runnable> = mutableListOf()
-
- override fun execute(runnable: Runnable) {
- runnables.add(runnable)
- }
-
- override fun executeDelayed(runnable: Runnable, delayMillis: Long) {
- execute(runnable)
- }
-
- override fun removeCallbacks(runnable: Runnable?) {}
-
- override fun hasCallback(runnable: Runnable?): Boolean = false
-
- fun flushAll() {
- while (runnables.isNotEmpty()) {
- runnables.removeAt(0).run()
- }
- }
- }
-
private class FakeBubblesStateListener : Bubbles.BubbleStateListener {
override fun onBubbleStateChange(update: BubbleBarUpdate?) {}
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 b38d00da..1d0c505 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
@@ -602,8 +602,72 @@
testGetBubbleBarExpandedViewBounds(onLeft = false, isOverflow = true)
}
+ @Test
+ fun getExpandedViewContainerPadding_largeScreen_fitsMaxViewWidth() {
+ val expandedViewWidth = context.resources.getDimensionPixelSize(
+ R.dimen.bubble_expanded_view_largescreen_width
+ )
+ // set the screen size so that it is wide enough to fit the maximum width size
+ val screenWidth = expandedViewWidth * 2
+ positioner.update(
+ defaultDeviceConfig.copy(
+ windowBounds = Rect(0, 0, screenWidth, 2000),
+ isLargeScreen = true,
+ isLandscape = false
+ )
+ )
+ val paddings =
+ positioner.getExpandedViewContainerPadding(/* onLeft= */ true, /* isOverflow= */ false)
+
+ val padding = context.resources.getDimensionPixelSize(
+ R.dimen.bubble_expanded_view_largescreen_landscape_padding
+ )
+ val right = screenWidth - expandedViewWidth - padding
+ assertThat(paddings).isEqualTo(intArrayOf(padding - positioner.pointerSize, 0, right, 0))
+ }
+
+ @Test
+ fun getExpandedViewContainerPadding_largeScreen_doesNotFitMaxViewWidth() {
+ positioner.update(
+ defaultDeviceConfig.copy(
+ windowBounds = Rect(0, 0, 600, 2000),
+ isLargeScreen = true,
+ isLandscape = false
+ )
+ )
+ val paddings =
+ positioner.getExpandedViewContainerPadding(/* onLeft= */ true, /* isOverflow= */ false)
+
+ val padding = context.resources.getDimensionPixelSize(
+ R.dimen.bubble_expanded_view_largescreen_landscape_padding
+ )
+ // the screen is not wide enough to fit the maximum width size, so the view fills the screen
+ // minus left and right padding
+ assertThat(paddings).isEqualTo(intArrayOf(padding - positioner.pointerSize, 0, padding, 0))
+ }
+
+ @Test
+ fun getExpandedViewContainerPadding_smallTablet() {
+ val screenWidth = 500
+ positioner.update(
+ defaultDeviceConfig.copy(
+ windowBounds = Rect(0, 0, screenWidth, 2000),
+ isLargeScreen = true,
+ isSmallTablet = true,
+ isLandscape = false
+ )
+ )
+ val paddings =
+ positioner.getExpandedViewContainerPadding(/* onLeft= */ true, /* isOverflow= */ false)
+
+ // for small tablets, the view width is set to be 0.72 * screen width
+ val viewWidth = (screenWidth * 0.72).toInt()
+ val padding = (screenWidth - viewWidth) / 2
+ assertThat(paddings).isEqualTo(intArrayOf(padding - positioner.pointerSize, 0, padding, 0))
+ }
+
private fun testGetBubbleBarExpandedViewBounds(onLeft: Boolean, isOverflow: Boolean) {
- positioner.setShowingInBubbleBar(true)
+ positioner.isShowingInBubbleBar = true
val windowBounds = Rect(0, 0, 2000, 2600)
val insets = Insets.of(10, 20, 5, 15)
val deviceConfig =
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
index 36acf66..239acd3 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -36,14 +36,11 @@
import com.android.launcher3.icons.BubbleIconFactory
import com.android.wm.shell.Flags
import com.android.wm.shell.R
+import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix
import com.android.wm.shell.common.FloatingContentCoordinator
-import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
-import com.android.wm.shell.shared.bubbles.BubbleBarLocation
-import com.android.wm.shell.taskview.TaskView
-import com.android.wm.shell.taskview.TaskViewTaskController
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.After
@@ -71,7 +68,7 @@
private lateinit var iconFactory: BubbleIconFactory
private lateinit var expandedViewManager: FakeBubbleExpandedViewManager
private lateinit var bubbleStackView: BubbleStackView
- private lateinit var shellExecutor: ShellExecutor
+ private lateinit var shellExecutor: TestShellExecutor
private lateinit var windowManager: WindowManager
private lateinit var bubbleTaskViewFactory: BubbleTaskViewFactory
private lateinit var bubbleData: BubbleData
@@ -108,7 +105,7 @@
)
bubbleStackViewManager = FakeBubbleStackViewManager()
expandedViewManager = FakeBubbleExpandedViewManager()
- bubbleTaskViewFactory = FakeBubbleTaskViewFactory()
+ bubbleTaskViewFactory = FakeBubbleTaskViewFactory(context, shellExecutor)
bubbleStackView =
BubbleStackView(
context,
@@ -168,6 +165,7 @@
// This will eventually propagate an update back to the stack view, but setting the
// entire pipeline is outside the scope of a unit test.
assertThat(bubbleData.isExpanded).isTrue()
+ shellExecutor.flushAll()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
@@ -206,6 +204,7 @@
bubbleStackView.setSelectedBubble(bubble2)
bubbleStackView.isExpanded = true
+ shellExecutor.flushAll()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
@@ -223,6 +222,7 @@
// tap on bubble1 to select it
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubble1.iconView!!.performClick()
+ shellExecutor.flushAll()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
assertThat(bubbleData.selectedBubble).isEqualTo(bubble1)
@@ -233,6 +233,7 @@
// listener wired up.
bubbleStackView.setSelectedBubble(bubble1)
bubble1.iconView!!.performClick()
+ shellExecutor.flushAll()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
@@ -426,55 +427,4 @@
override fun hideCurrentInputMethod() {}
}
-
- private class TestShellExecutor : ShellExecutor {
-
- override fun execute(runnable: Runnable) {
- runnable.run()
- }
-
- override fun executeDelayed(r: Runnable, delayMillis: Long) {
- r.run()
- }
-
- override fun removeCallbacks(r: Runnable?) {}
-
- override fun hasCallback(r: Runnable): Boolean = false
- }
-
- private inner class FakeBubbleTaskViewFactory : BubbleTaskViewFactory {
- override fun create(): BubbleTaskView {
- val taskViewTaskController = mock<TaskViewTaskController>()
- val taskView = TaskView(context, taskViewTaskController)
- return BubbleTaskView(taskView, shellExecutor)
- }
- }
-
- private inner class FakeBubbleExpandedViewManager : BubbleExpandedViewManager {
-
- override val overflowBubbles: List<Bubble>
- get() = emptyList()
-
- override fun setOverflowListener(listener: BubbleData.Listener) {}
-
- override fun collapseStack() {}
-
- override fun updateWindowFlagsForBackpress(intercept: Boolean) {}
-
- override fun promoteBubbleFromOverflow(bubble: Bubble) {}
-
- override fun removeBubble(key: String, reason: Int) {}
-
- override fun dismissBubble(bubble: Bubble, reason: Int) {}
-
- override fun setAppBubbleTaskId(key: String, taskId: Int) {}
-
- override fun isStackExpanded(): Boolean = false
-
- override fun isShowingAsBubbleBar(): Boolean = false
-
- override fun hideCurrentInputMethod() {}
-
- override fun updateBubbleBarLocation(location: BubbleBarLocation, source: Int) {}
- }
}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
index 776ea92..680d015 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
@@ -35,13 +35,13 @@
import com.android.internal.statusbar.IStatusBarService
import com.android.launcher3.icons.BubbleIconFactory
import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.WindowManagerShellWrapper
import com.android.wm.shell.bubbles.properties.BubbleProperties
import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayInsetsController
import com.android.wm.shell.common.FloatingContentCoordinator
-import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.common.TaskStackListenerImpl
import com.android.wm.shell.shared.TransactionPool
@@ -70,8 +70,8 @@
private lateinit var metadataFlagListener: Bubbles.BubbleMetadataFlagListener
private lateinit var iconFactory: BubbleIconFactory
private lateinit var bubbleController: BubbleController
- private lateinit var mainExecutor: TestExecutor
- private lateinit var bgExecutor: TestExecutor
+ private lateinit var mainExecutor: TestShellExecutor
+ private lateinit var bgExecutor: TestShellExecutor
private lateinit var bubbleStackView: BubbleStackView
private lateinit var bubblePositioner: BubblePositioner
private lateinit var bubbleLogger: BubbleLogger
@@ -94,8 +94,8 @@
context.resources.getDimensionPixelSize(R.dimen.importance_ring_stroke_width)
)
- mainExecutor = TestExecutor()
- bgExecutor = TestExecutor()
+ mainExecutor = TestShellExecutor()
+ bgExecutor = TestShellExecutor()
val windowManager = context.getSystemService(WindowManager::class.java)
val shellInit = ShellInit(mainExecutor)
val shellCommandHandler = ShellCommandHandler()
@@ -335,27 +335,4 @@
bgExecutor
)
}
-
- private class TestExecutor : ShellExecutor {
-
- private val runnables: MutableList<Runnable> = mutableListOf()
-
- override fun execute(runnable: Runnable) {
- runnables.add(runnable)
- }
-
- override fun executeDelayed(runnable: Runnable, delayMillis: Long) {
- execute(runnable)
- }
-
- override fun removeCallbacks(runnable: Runnable?) {}
-
- override fun hasCallback(runnable: Runnable?): Boolean = false
-
- fun flushAll() {
- while (runnables.isNotEmpty()) {
- runnables.removeAt(0).run()
- }
- }
- }
}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleExpandedViewManager.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleExpandedViewManager.kt
new file mode 100644
index 0000000..3c013d3
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleExpandedViewManager.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.wm.shell.bubbles
+
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
+import java.util.Collections
+
+/** Fake implementation of [BubbleExpandedViewManager] for testing. */
+class FakeBubbleExpandedViewManager(var bubbleBar: Boolean = false, var expanded: Boolean = false) :
+ BubbleExpandedViewManager {
+
+ override val overflowBubbles: List<Bubble>
+ get() = Collections.emptyList()
+
+ override fun setOverflowListener(listener: BubbleData.Listener) {}
+
+ override fun collapseStack() {}
+
+ override fun updateWindowFlagsForBackpress(intercept: Boolean) {}
+
+ override fun promoteBubbleFromOverflow(bubble: Bubble) {}
+
+ override fun removeBubble(key: String, reason: Int) {}
+
+ override fun dismissBubble(bubble: Bubble, reason: Int) {}
+
+ override fun setAppBubbleTaskId(key: String, taskId: Int) {}
+
+ override fun isStackExpanded(): Boolean {
+ return expanded
+ }
+
+ override fun isShowingAsBubbleBar(): Boolean {
+ return bubbleBar
+ }
+
+ override fun hideCurrentInputMethod() {}
+
+ override fun updateBubbleBarLocation(location: BubbleBarLocation, source: Int) {}
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt
index 3279d56..bcaa63b 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt
@@ -19,6 +19,10 @@
import android.content.Context
import android.content.pm.ShortcutInfo
import android.content.res.Resources
+import android.view.LayoutInflater
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.wm.shell.R
+import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.bubbles.BubbleViewInfoTask.BubbleViewInfo
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView
import com.google.common.util.concurrent.MoreExecutors.directExecutor
@@ -27,6 +31,33 @@
class FakeBubbleFactory {
companion object {
+ fun createExpandedView(
+ context: Context,
+ bubblePositioner: BubblePositioner,
+ expandedViewManager: BubbleExpandedViewManager,
+ bubbleTaskView: BubbleTaskView,
+ mainExecutor: TestShellExecutor,
+ bgExecutor: TestShellExecutor,
+ bubbleLogger: BubbleLogger = BubbleLogger(UiEventLoggerFake()),
+ ): BubbleBarExpandedView {
+ val bubbleBarExpandedView =
+ (LayoutInflater.from(context)
+ .inflate(R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */)
+ as BubbleBarExpandedView)
+ .apply {
+ initialize(
+ expandedViewManager,
+ bubblePositioner,
+ bubbleLogger,
+ false, /* isOverflow */
+ bubbleTaskView,
+ mainExecutor,
+ bgExecutor,
+ null, /* regionSamplingProvider */
+ )
+ }
+ return bubbleBarExpandedView
+ }
fun createViewInfo(bubbleExpandedView: BubbleBarExpandedView): BubbleViewInfo {
return BubbleViewInfo().apply { bubbleBarExpandedView = bubbleExpandedView }
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleTaskViewFactory.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleTaskViewFactory.kt
new file mode 100644
index 0000000..42b66aa
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleTaskViewFactory.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.wm.shell.bubbles
+
+import android.app.ActivityManager
+import android.content.Context
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.taskview.TaskView
+import com.android.wm.shell.taskview.TaskViewTaskController
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+/**
+ * Implementation of [BubbleTaskViewFactory] for testing.
+ */
+class FakeBubbleTaskViewFactory(
+ private val context: Context,
+ private val mainExecutor: ShellExecutor,
+) : BubbleTaskViewFactory {
+ override fun create(): BubbleTaskView {
+ val taskViewTaskController = mock<TaskViewTaskController>()
+ val taskView = TaskView(context, taskViewTaskController)
+ val taskInfo = mock<ActivityManager.RunningTaskInfo>()
+ whenever(taskViewTaskController.taskInfo).thenReturn(taskInfo)
+ return BubbleTaskView(taskView, mainExecutor)
+ }
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
new file mode 100644
index 0000000..0d8f809
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.bubbles.bar
+
+import android.content.Context
+import android.graphics.Insets
+import android.graphics.Rect
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager
+import android.widget.FrameLayout
+import androidx.core.animation.AnimatorTestRule
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.bubbles.Bubble
+import com.android.wm.shell.bubbles.BubbleExpandedViewManager
+import com.android.wm.shell.bubbles.BubbleLogger
+import com.android.wm.shell.bubbles.BubbleOverflow
+import com.android.wm.shell.bubbles.BubblePositioner
+import com.android.wm.shell.bubbles.DeviceConfig
+import com.android.wm.shell.bubbles.FakeBubbleExpandedViewManager
+import com.android.wm.shell.bubbles.FakeBubbleFactory
+import com.android.wm.shell.bubbles.FakeBubbleTaskViewFactory
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Semaphore
+import java.util.concurrent.TimeUnit
+import org.junit.After
+import org.junit.Before
+import org.junit.ClassRule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Tests for [BubbleBarAnimationHelper] */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BubbleBarAnimationHelperTest {
+
+ companion object {
+ @JvmField @ClassRule val animatorTestRule: AnimatorTestRule = AnimatorTestRule()
+
+ const val SCREEN_WIDTH = 2000
+ const val SCREEN_HEIGHT = 1000
+ }
+
+ private val context = ApplicationProvider.getApplicationContext<Context>()
+
+ private lateinit var animationHelper: BubbleBarAnimationHelper
+ private lateinit var bubblePositioner: BubblePositioner
+ private lateinit var expandedViewManager: BubbleExpandedViewManager
+ private lateinit var bubbleLogger: BubbleLogger
+ private lateinit var mainExecutor: TestShellExecutor
+ private lateinit var bgExecutor: TestShellExecutor
+ private lateinit var container: FrameLayout
+
+ @Before
+ fun setUp() {
+ ProtoLog.REQUIRE_PROTOLOGTOOL = false
+ ProtoLog.init()
+ val windowManager = context.getSystemService(WindowManager::class.java)
+ bubblePositioner = BubblePositioner(context, windowManager)
+ bubblePositioner.setShowingInBubbleBar(true)
+ val deviceConfig =
+ DeviceConfig(
+ windowBounds = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
+ isLargeScreen = true,
+ isSmallTablet = false,
+ isLandscape = true,
+ isRtl = false,
+ insets = Insets.of(10, 20, 30, 40),
+ )
+ bubblePositioner.update(deviceConfig)
+ expandedViewManager = FakeBubbleExpandedViewManager()
+ bubbleLogger = BubbleLogger(UiEventLoggerFake())
+
+ mainExecutor = TestShellExecutor()
+ bgExecutor = TestShellExecutor()
+
+ container = FrameLayout(context)
+
+ animationHelper = BubbleBarAnimationHelper(context, bubblePositioner)
+ }
+
+ @After
+ fun tearDown() {
+ bgExecutor.flushAll()
+ mainExecutor.flushAll()
+ }
+
+ @Test
+ fun animateSwitch_bubbleToBubble_oldHiddenNewShown() {
+ val fromBubble = createBubble(key = "from").initialize(container)
+ val toBubble = createBubble(key = "to").initialize(container)
+
+ val semaphore = Semaphore(0)
+ val after = Runnable { semaphore.release() }
+
+ getInstrumentation().runOnMainSync {
+ animationHelper.animateSwitch(fromBubble, toBubble, after)
+ animatorTestRule.advanceTimeBy(1000)
+ }
+ getInstrumentation().waitForIdleSync()
+
+ assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+ assertThat(fromBubble.bubbleBarExpandedView?.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(fromBubble.bubbleBarExpandedView?.alpha).isEqualTo(0f)
+ assertThat(fromBubble.bubbleBarExpandedView?.isSurfaceZOrderedOnTop).isFalse()
+
+ assertThat(toBubble.bubbleBarExpandedView?.visibility).isEqualTo(View.VISIBLE)
+ assertThat(toBubble.bubbleBarExpandedView?.alpha).isEqualTo(1f)
+ assertThat(toBubble.bubbleBarExpandedView?.isSurfaceZOrderedOnTop).isFalse()
+ }
+
+ @Test
+ fun animateSwitch_bubbleToBubble_handleColorTransferred() {
+ val fromBubble = createBubble(key = "from").initialize(container)
+ fromBubble.bubbleBarExpandedView!!
+ .handleView
+ .updateHandleColor(/* isRegionDark= */ true, /* animated= */ false)
+ val toBubble = createBubble(key = "to").initialize(container)
+
+ getInstrumentation().runOnMainSync {
+ animationHelper.animateSwitch(fromBubble, toBubble, /* afterAnimation= */ null)
+ animatorTestRule.advanceTimeBy(1000)
+ }
+ getInstrumentation().waitForIdleSync()
+
+ assertThat(toBubble.bubbleBarExpandedView!!.handleView.handleColor)
+ .isEqualTo(fromBubble.bubbleBarExpandedView!!.handleView.handleColor)
+ }
+
+ @Test
+ fun animateSwitch_bubbleToOverflow_oldHiddenNewShown() {
+ val fromBubble = createBubble(key = "from").initialize(container)
+ val overflow = createOverflow().initialize(container)
+
+ val semaphore = Semaphore(0)
+ val after = Runnable { semaphore.release() }
+
+ getInstrumentation().runOnMainSync {
+ animationHelper.animateSwitch(fromBubble, overflow, after)
+ animatorTestRule.advanceTimeBy(1000)
+ }
+ getInstrumentation().waitForIdleSync()
+
+ assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+ assertThat(fromBubble.bubbleBarExpandedView?.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(fromBubble.bubbleBarExpandedView?.alpha).isEqualTo(0f)
+ assertThat(fromBubble.bubbleBarExpandedView?.isSurfaceZOrderedOnTop).isFalse()
+
+ assertThat(overflow.bubbleBarExpandedView?.visibility).isEqualTo(View.VISIBLE)
+ assertThat(overflow.bubbleBarExpandedView?.alpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun animateSwitch_overflowToBubble_oldHiddenNewShown() {
+ val overflow = createOverflow().initialize(container)
+ val toBubble = createBubble(key = "to").initialize(container)
+
+ val semaphore = Semaphore(0)
+ val after = Runnable { semaphore.release() }
+
+ getInstrumentation().runOnMainSync {
+ animationHelper.animateSwitch(overflow, toBubble, after)
+ animatorTestRule.advanceTimeBy(1000)
+ }
+ getInstrumentation().waitForIdleSync()
+
+ assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+ assertThat(overflow.bubbleBarExpandedView?.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(overflow.bubbleBarExpandedView?.alpha).isEqualTo(0f)
+
+ assertThat(toBubble.bubbleBarExpandedView?.visibility).isEqualTo(View.VISIBLE)
+ assertThat(toBubble.bubbleBarExpandedView?.alpha).isEqualTo(1f)
+ assertThat(toBubble.bubbleBarExpandedView?.isSurfaceZOrderedOnTop).isFalse()
+ }
+
+ private fun createBubble(key: String): Bubble {
+ val bubbleBarExpandedView =
+ FakeBubbleFactory.createExpandedView(
+ context,
+ bubblePositioner,
+ expandedViewManager,
+ FakeBubbleTaskViewFactory(context, mainExecutor).create(),
+ mainExecutor,
+ bgExecutor,
+ bubbleLogger,
+ )
+ val viewInfo = FakeBubbleFactory.createViewInfo(bubbleBarExpandedView)
+ return FakeBubbleFactory.createChatBubble(context, key, viewInfo)
+ }
+
+ private fun createOverflow(): BubbleOverflow {
+ val overflow = BubbleOverflow(context, bubblePositioner)
+ overflow.initializeForBubbleBar(expandedViewManager, bubblePositioner, bubbleLogger)
+ return overflow
+ }
+
+ private fun Bubble.initialize(container: ViewGroup): Bubble {
+ getInstrumentation().runOnMainSync { container.addView(bubbleBarExpandedView) }
+ // Mark taskView's visible
+ bubbleBarExpandedView!!.onContentVisibilityChanged(true)
+ return this
+ }
+
+ private fun BubbleOverflow.initialize(container: ViewGroup): BubbleOverflow {
+ getInstrumentation().runOnMainSync { container.addView(bubbleBarExpandedView) }
+ return this
+ }
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
index 1bf6af8..bfc798b 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
@@ -33,32 +33,30 @@
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.R
+import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.bubbles.Bubble
-import com.android.wm.shell.bubbles.BubbleData
import com.android.wm.shell.bubbles.BubbleExpandedViewManager
import com.android.wm.shell.bubbles.BubbleLogger
import com.android.wm.shell.bubbles.BubblePositioner
import com.android.wm.shell.bubbles.BubbleTaskView
import com.android.wm.shell.bubbles.BubbleTaskViewFactory
import com.android.wm.shell.bubbles.DeviceConfig
+import com.android.wm.shell.bubbles.FakeBubbleExpandedViewManager
import com.android.wm.shell.bubbles.RegionSamplingProvider
import com.android.wm.shell.bubbles.UiEventSubject.Companion.assertThat
-import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.handles.RegionSamplingHelper
import com.android.wm.shell.taskview.TaskView
import com.android.wm.shell.taskview.TaskViewTaskController
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import com.google.common.util.concurrent.MoreExecutors.directExecutor
-import java.util.Collections
-import java.util.concurrent.Executor
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
+import java.util.concurrent.Executor
/** Tests for [BubbleBarExpandedViewTest] */
@SmallTest
@@ -72,8 +70,8 @@
private val context = ApplicationProvider.getApplicationContext<Context>()
private val windowManager = context.getSystemService(WindowManager::class.java)
- private lateinit var mainExecutor: TestExecutor
- private lateinit var bgExecutor: TestExecutor
+ private lateinit var mainExecutor: TestShellExecutor
+ private lateinit var bgExecutor: TestShellExecutor
private lateinit var expandedViewManager: BubbleExpandedViewManager
private lateinit var positioner: BubblePositioner
@@ -90,8 +88,8 @@
fun setUp() {
ProtoLog.REQUIRE_PROTOLOGTOOL = false
ProtoLog.init()
- mainExecutor = TestExecutor()
- bgExecutor = TestExecutor()
+ mainExecutor = TestShellExecutor()
+ bgExecutor = TestShellExecutor()
positioner = BubblePositioner(context, windowManager)
positioner.setShowingInBubbleBar(true)
val deviceConfig =
@@ -105,7 +103,7 @@
)
positioner.update(deviceConfig)
- expandedViewManager = createExpandedViewManager()
+ expandedViewManager = FakeBubbleExpandedViewManager(bubbleBar = true, expanded = true)
bubbleTaskView = FakeBubbleTaskViewFactory().create()
val inflater = LayoutInflater.from(context)
@@ -426,63 +424,4 @@
setWindowInvisible = false
}
}
-
- private fun createExpandedViewManager(): BubbleExpandedViewManager {
- return object : BubbleExpandedViewManager {
- override val overflowBubbles: List<Bubble>
- get() = Collections.emptyList()
-
- override fun setOverflowListener(listener: BubbleData.Listener) {
- }
-
- override fun collapseStack() {
- }
-
- override fun updateWindowFlagsForBackpress(intercept: Boolean) {
- }
-
- override fun promoteBubbleFromOverflow(bubble: Bubble) {
- }
-
- override fun removeBubble(key: String, reason: Int) {
- }
-
- override fun dismissBubble(bubble: Bubble, reason: Int) {
- }
-
- override fun setAppBubbleTaskId(key: String, taskId: Int) {
- }
-
- override fun isStackExpanded(): Boolean {
- return true
- }
-
- override fun isShowingAsBubbleBar(): Boolean {
- return true
- }
-
- override fun hideCurrentInputMethod() {
- }
-
- override fun updateBubbleBarLocation(location: BubbleBarLocation, source: Int) {
- }
- }
- }
-
- private class TestExecutor : ShellExecutor {
-
- private val runnables: MutableList<Runnable> = mutableListOf()
-
- override fun execute(runnable: Runnable) {
- runnables.add(runnable)
- }
-
- override fun executeDelayed(runnable: Runnable, delayMillis: Long) {
- execute(runnable)
- }
-
- override fun removeCallbacks(runnable: Runnable?) {}
-
- override fun hasCallback(runnable: Runnable?): Boolean = false
- }
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt
index 7280f8a..04c9ffb 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt
@@ -16,14 +16,12 @@
package com.android.wm.shell.bubbles.bar
-import android.app.ActivityManager
import android.content.Context
import android.content.pm.LauncherApps
import android.graphics.PointF
import android.os.Handler
import android.os.UserManager
import android.view.IWindowManager
-import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
@@ -37,19 +35,19 @@
import com.android.internal.statusbar.IStatusBarService
import com.android.wm.shell.R
import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.WindowManagerShellWrapper
import com.android.wm.shell.bubbles.Bubble
import com.android.wm.shell.bubbles.BubbleController
import com.android.wm.shell.bubbles.BubbleData
import com.android.wm.shell.bubbles.BubbleDataRepository
import com.android.wm.shell.bubbles.BubbleEducationController
-import com.android.wm.shell.bubbles.BubbleExpandedViewManager
import com.android.wm.shell.bubbles.BubbleLogger
import com.android.wm.shell.bubbles.BubblePositioner
-import com.android.wm.shell.bubbles.BubbleTaskView
-import com.android.wm.shell.bubbles.BubbleTaskViewFactory
import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
+import com.android.wm.shell.bubbles.FakeBubbleExpandedViewManager
import com.android.wm.shell.bubbles.FakeBubbleFactory
+import com.android.wm.shell.bubbles.FakeBubbleTaskViewFactory
import com.android.wm.shell.bubbles.UiEventSubject.Companion.assertThat
import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix
import com.android.wm.shell.bubbles.properties.BubbleProperties
@@ -57,7 +55,6 @@
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayInsetsController
import com.android.wm.shell.common.FloatingContentCoordinator
-import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.common.TaskStackListenerImpl
import com.android.wm.shell.shared.TransactionPool
@@ -66,20 +63,16 @@
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
-import com.android.wm.shell.taskview.TaskView
-import com.android.wm.shell.taskview.TaskViewTaskController
import com.android.wm.shell.taskview.TaskViewTransitions
import com.android.wm.shell.transition.Transitions
import com.google.common.truth.Truth.assertThat
import org.junit.After
-import java.util.Collections
import org.junit.Before
import org.junit.ClassRule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import org.mockito.kotlin.mock
-import org.mockito.kotlin.whenever
/** Tests for [BubbleBarLayerView] */
@SmallTest
@@ -87,8 +80,7 @@
class BubbleBarLayerViewTest {
companion object {
- @JvmField @ClassRule
- val animatorTestRule: AnimatorTestRule = AnimatorTestRule()
+ @JvmField @ClassRule val animatorTestRule: AnimatorTestRule = AnimatorTestRule()
}
private val context = ApplicationProvider.getApplicationContext<Context>()
@@ -112,8 +104,8 @@
uiEventLoggerFake = UiEventLoggerFake()
val bubbleLogger = BubbleLogger(uiEventLoggerFake)
- val mainExecutor = TestExecutor()
- val bgExecutor = TestExecutor()
+ val mainExecutor = TestShellExecutor()
+ val bgExecutor = TestShellExecutor()
val windowManager = context.getSystemService(WindowManager::class.java)
@@ -145,24 +137,18 @@
bubbleBarLayerView = BubbleBarLayerView(context, bubbleController, bubbleData, bubbleLogger)
- val expandedViewManager = createExpandedViewManager()
- val bubbleTaskView = FakeBubbleTaskViewFactory(mainExecutor).create()
+ val expandedViewManager = FakeBubbleExpandedViewManager(bubbleBar = true, expanded = true)
+ val bubbleTaskView = FakeBubbleTaskViewFactory(context, mainExecutor).create()
val bubbleBarExpandedView =
- (LayoutInflater.from(context)
- .inflate(R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */)
- as BubbleBarExpandedView)
- .apply {
- initialize(
- expandedViewManager,
- bubblePositioner,
- bubbleLogger,
- false /* isOverflow */,
- bubbleTaskView,
- mainExecutor,
- bgExecutor,
- null, /* regionSamplingProvider */
- )
- }
+ FakeBubbleFactory.createExpandedView(
+ context,
+ bubblePositioner,
+ expandedViewManager,
+ bubbleTaskView,
+ mainExecutor,
+ bgExecutor,
+ bubbleLogger,
+ )
val viewInfo = FakeBubbleFactory.createViewInfo(bubbleBarExpandedView)
bubble = FakeBubbleFactory.createChatBubble(context, viewInfo = viewInfo)
@@ -179,8 +165,8 @@
windowManager: WindowManager?,
bubbleLogger: BubbleLogger,
bubblePositioner: BubblePositioner,
- mainExecutor: TestExecutor,
- bgExecutor: TestExecutor,
+ mainExecutor: TestShellExecutor,
+ bgExecutor: TestShellExecutor,
): BubbleController {
val shellInit = ShellInit(mainExecutor)
val shellCommandHandler = ShellCommandHandler()
@@ -310,74 +296,9 @@
getInstrumentation().waitForIdleSync()
getInstrumentation().runOnMainSync { animatorTestRule.advanceTimeBy(200) }
PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(
- AnimatableScaleMatrix.SCALE_X, AnimatableScaleMatrix.SCALE_Y)
- }
-
- private inner class FakeBubbleTaskViewFactory(private val mainExecutor: ShellExecutor) :
- BubbleTaskViewFactory {
- override fun create(): BubbleTaskView {
- val taskViewTaskController = mock<TaskViewTaskController>()
- val taskView = TaskView(context, taskViewTaskController)
- val taskInfo = mock<ActivityManager.RunningTaskInfo>()
- whenever(taskViewTaskController.taskInfo).thenReturn(taskInfo)
- return BubbleTaskView(taskView, mainExecutor)
- }
- }
-
- private fun createExpandedViewManager(): BubbleExpandedViewManager {
- return object : BubbleExpandedViewManager {
- override val overflowBubbles: List<Bubble>
- get() = Collections.emptyList()
-
- override fun setOverflowListener(listener: BubbleData.Listener) {}
-
- override fun collapseStack() {}
-
- override fun updateWindowFlagsForBackpress(intercept: Boolean) {}
-
- override fun promoteBubbleFromOverflow(bubble: Bubble) {}
-
- override fun removeBubble(key: String, reason: Int) {}
-
- override fun dismissBubble(bubble: Bubble, reason: Int) {}
-
- override fun setAppBubbleTaskId(key: String, taskId: Int) {}
-
- override fun isStackExpanded(): Boolean {
- return true
- }
-
- override fun isShowingAsBubbleBar(): Boolean {
- return true
- }
-
- override fun hideCurrentInputMethod() {}
-
- override fun updateBubbleBarLocation(location: BubbleBarLocation, source: Int) {}
- }
- }
-
- private class TestExecutor : ShellExecutor {
-
- private val runnables: MutableList<Runnable> = mutableListOf()
-
- override fun execute(runnable: Runnable) {
- runnables.add(runnable)
- }
-
- override fun executeDelayed(runnable: Runnable, delayMillis: Long) {
- execute(runnable)
- }
-
- override fun removeCallbacks(runnable: Runnable?) {}
-
- override fun hasCallback(runnable: Runnable?): Boolean = false
-
- fun flushAll() {
- while (runnables.isNotEmpty()) {
- runnables.removeAt(0).run()
- }
- }
+ AnimatableScaleMatrix.SCALE_X,
+ AnimatableScaleMatrix.SCALE_Y,
+ )
}
private fun View.dispatchTouchEvent(eventTime: Long, action: Int, point: PointF) {
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index bb53adf..244149b 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -87,7 +87,7 @@
<string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Upravljajte oblačićima u svakom trenutku"</string>
<string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Dodirnite ovdje da upravljate time koje aplikacije i razgovori mogu imati oblačić"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
- <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljaj"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljajte"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
<string name="bubble_shortcut_label" msgid="666269077944378311">"Oblačići"</string>
<string name="bubble_shortcut_long_label" msgid="6088437544312894043">"Prikaz oblačića"</string>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 21ec84d..9e2d23b 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -266,6 +266,8 @@
<dimen name="bubble_bar_expanded_view_handle_height">4dp</dimen>
<!-- Width of the expanded bubble bar view shown when the bubble is expanded. -->
<dimen name="bubble_bar_expanded_view_width">412dp</dimen>
+ <!-- Offset of the expanded view when it starts sliding in as part of the switch animation -->
+ <dimen name="bubble_bar_expanded_view_switch_offset">48dp</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. -->
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
index 04c17e5..4c77eaf 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
@@ -91,6 +91,9 @@
/** The maximum override density allowed for tasks inside the desktop. */
private static final int DESKTOP_DENSITY_MAX = 1000;
+ /** The number of [WindowDecorViewHost] instances to warm up on system start. */
+ private static final int WINDOW_DECOR_PRE_WARM_SIZE = 2;
+
/**
* Sysprop declaring whether to enters desktop mode by default when the windowing mode of the
* display's root TaskDisplayArea is set to WINDOWING_MODE_FREEFORM.
@@ -122,6 +125,14 @@
private static final String MAX_TASK_LIMIT_SYS_PROP = "persist.wm.debug.desktop_max_task_limit";
/**
+ * Sysprop declaring the number of [WindowDecorViewHost] instances to warm up on system start.
+ *
+ * <p>If it is not defined, then [WINDOW_DECOR_PRE_WARM_SIZE] is used.
+ */
+ private static final String WINDOW_DECOR_PRE_WARM_SIZE_SYS_PROP =
+ "persist.wm.debug.desktop_window_decor_pre_warm_size";
+
+ /**
* Return {@code true} if veiled resizing is active. If false, fluid resizing is used.
*/
public static boolean isVeiledResizeEnabled() {
@@ -162,6 +173,27 @@
}
/**
+ * Return the maximum size of the window decoration surface control view host pool, or zero if
+ * there should be no pooling.
+ */
+ public static int getWindowDecorScvhPoolSize(@NonNull Context context) {
+ if (!Flags.enableDesktopWindowingScvhCacheBugFix()) return 0;
+ final int maxTaskLimit = getMaxTaskLimit(context);
+ if (maxTaskLimit > 0) {
+ return maxTaskLimit;
+ }
+ // TODO: b/368032552 - task limit equal to 0 means unlimited. Figure out what the pool
+ // size should be in that case.
+ return 0;
+ }
+
+ /** The number of [WindowDecorViewHost] instances to warm up on system start. */
+ public static int getWindowDecorPreWarmSize() {
+ return SystemProperties.getInt(WINDOW_DECOR_PRE_WARM_SIZE_SYS_PROP,
+ WINDOW_DECOR_PRE_WARM_SIZE);
+ }
+
+ /**
* Return {@code true} if the current device supports desktop mode.
*/
@VisibleForTesting
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
index d2cef4b..f269b38 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
@@ -268,10 +268,7 @@
final Animation animation =
animationProvider.get(info, change, openingWholeScreenBounds);
if (shouldUseJumpCutForAnimation(animation)) {
- if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- return new ArrayList<>();
- }
- continue;
+ return new ArrayList<>();
}
final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter(
info, change, animation, openingWholeScreenBounds);
@@ -296,10 +293,7 @@
final Animation animation =
animationProvider.get(info, change, closingWholeScreenBounds);
if (shouldUseJumpCutForAnimation(animation)) {
- if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- return new ArrayList<>();
- }
- continue;
+ return new ArrayList<>();
}
final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter(
info, change, animation, closingWholeScreenBounds);
@@ -455,11 +449,9 @@
final Animation[] animations =
mAnimationSpec.createChangeBoundsChangeAnimations(info, change, parentBounds);
// Jump cut if either animation has zero for duration.
- if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- for (Animation animation : animations) {
- if (shouldUseJumpCutForAnimation(animation)) {
- return new ArrayList<>();
- }
+ for (Animation animation : animations) {
+ if (shouldUseJumpCutForAnimation(animation)) {
+ return new ArrayList<>();
}
}
// Keep track as we might need to add background color for the animation.
@@ -516,10 +508,8 @@
mAnimationSpec.createChangeBoundsOpenAnimation(info, change, parentBounds);
shouldShowBackgroundColor = false;
}
- if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- if (shouldUseJumpCutForAnimation(animation)) {
- return new ArrayList<>();
- }
+ if (shouldUseJumpCutForAnimation(animation)) {
+ return new ArrayList<>();
}
adapters.add(new ActivityEmbeddingAnimationAdapter(animation, change,
TransitionUtil.getRootFor(change, info)));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
index 3046307..77799e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
@@ -96,11 +96,9 @@
@NonNull
Animation createChangeBoundsOpenAnimation(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) {
- if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
- if (customAnimation != null) {
- return customAnimation;
- }
+ final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
+ if (customAnimation != null) {
+ return customAnimation;
}
// Use end bounds for opening.
final Rect bounds = change.getEndAbsBounds();
@@ -130,11 +128,9 @@
@NonNull
Animation createChangeBoundsCloseAnimation(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) {
- if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
- if (customAnimation != null) {
- return customAnimation;
- }
+ final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
+ if (customAnimation != null) {
+ return customAnimation;
}
// Use start bounds for closing.
final Rect bounds = change.getStartAbsBounds();
@@ -168,14 +164,12 @@
@NonNull
Animation[] createChangeBoundsChangeAnimations(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) {
- if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- // TODO(b/293658614): Support more complicated animations that may need more than a noop
- // animation as the start leash.
- final Animation noopAnimation = createNoopAnimation(change);
- final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
- if (customAnimation != null) {
- return new Animation[]{noopAnimation, customAnimation};
- }
+ // TODO(b/293658614): Support more complicated animations that may need more than a noop
+ // animation as the start leash.
+ final Animation noopAnimation = createNoopAnimation(change);
+ final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
+ if (customAnimation != null) {
+ return new Animation[]{noopAnimation, customAnimation};
}
// Both start bounds and end bounds are in screen coordinates. We will post translate
// to the local coordinates in ActivityEmbeddingAnimationAdapter#onAnimationUpdate
@@ -320,13 +314,9 @@
}
final Animation anim;
- if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- // TODO(b/293658614): Consider allowing custom animations from non-default packages.
- // Enforce limiting to animations from the default "android" package for now.
- anim = mTransitionAnimation.loadDefaultAnimationRes(resId);
- } else {
- anim = mTransitionAnimation.loadAnimationRes(options.getPackageName(), resId);
- }
+ // TODO(b/293658614): Consider allowing custom animations from non-default packages.
+ // Enforce limiting to animations from the default "android" package for now.
+ anim = mTransitionAnimation.loadDefaultAnimationRes(resId);
if (anim != null) {
return anim;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/OWNERS
new file mode 100644
index 0000000..84596b0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/OWNERS
@@ -0,0 +1,6 @@
+# WM shell sub-module automotive owners
+
+winsonc@google.com
+stenning@google.com
+gauravbhola@google.com
+xiangw@google.com
\ No newline at end of file
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 c024840..60a52a8 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
@@ -214,6 +214,10 @@
}
ProtoLog.i(WM_SHELL_BACK_PREVIEW, "Navigation window gone.");
setTriggerBack(false);
+ // Trigger close transition if necessary.
+ if (Flags.migratePredictiveBackTransition()) {
+ mBackTransitionHandler.onAnimationFinished();
+ }
resetTouchTracker();
// Don't wait for animation start
mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable);
@@ -731,6 +735,8 @@
callback.onBackStarted(backEvent);
if (mBackTransitionHandler.canHandOffAnimation()) {
callback.setHandoffHandler(mHandoffHandler);
+ } else {
+ callback.setHandoffHandler(null);
}
mOnBackStartDispatched = true;
} catch (RemoteException e) {
@@ -1273,14 +1279,6 @@
@NonNull SurfaceControl.Transaction st,
@NonNull SurfaceControl.Transaction ft,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- final boolean isPrepareTransition =
- info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
- if (isPrepareTransition) {
- if (checkTakeoverFlags()) {
- mTakeoverHandler = mTransitions.getHandlerForTakeover(transition, info);
- }
- kickStartAnimation();
- }
// Both mShellExecutor and Transitions#mMainExecutor are ShellMainThread, so we don't
// need to post to ShellExecutor when called.
if (info.getType() == WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION) {
@@ -1308,7 +1306,8 @@
// animation never start, consume directly
applyAndFinish(st, ft, finishCallback);
return true;
- } else if (mClosePrepareTransition == null && isPrepareTransition) {
+ } else if (mClosePrepareTransition == null
+ && info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION) {
// Gesture animation was cancelled before prepare transition ready, create
// the close prepare transition
createClosePrepareTransition();
@@ -1316,6 +1315,10 @@
}
if (handlePrepareTransition(info, st, ft, finishCallback)) {
+ if (checkTakeoverFlags()) {
+ mTakeoverHandler = mTransitions.getHandlerForTakeover(transition, info);
+ }
+ kickStartAnimation();
return true;
}
return handleCloseTransition(info, st, ft, finishCallback);
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 0fd4206..de85d9a 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
@@ -163,8 +163,11 @@
mExpandedViewLargeScreenWidth = (int) (bounds.width()
* EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT);
} else {
- mExpandedViewLargeScreenWidth =
- res.getDimensionPixelSize(R.dimen.bubble_expanded_view_largescreen_width);
+ int expandedViewLargeScreenSpacing = res.getDimensionPixelSize(
+ R.dimen.bubble_expanded_view_largescreen_landscape_padding);
+ mExpandedViewLargeScreenWidth = Math.min(
+ res.getDimensionPixelSize(R.dimen.bubble_expanded_view_largescreen_width),
+ bounds.width() - expandedViewLargeScreenSpacing * 2);
}
if (mDeviceConfig.isLargeScreen()) {
if (mDeviceConfig.isSmallTablet()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index a313bd0..3e8a9b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.bubbles.bar;
import static android.view.View.ALPHA;
+import static android.view.View.INVISIBLE;
import static android.view.View.SCALE_X;
import static android.view.View.SCALE_Y;
import static android.view.View.TRANSLATION_X;
@@ -25,6 +26,7 @@
import static android.view.View.Y;
import static com.android.wm.shell.bubbles.bar.BubbleBarExpandedView.CORNER_RADIUS;
+import static com.android.wm.shell.bubbles.bar.BubbleBarExpandedView.TASK_VIEW_ALPHA;
import static com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED;
import static com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED_DECELERATE;
@@ -32,7 +34,6 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
@@ -42,11 +43,12 @@
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
+import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.BubbleOverflow;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleViewProvider;
import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix;
-import com.android.wm.shell.shared.animation.Interpolators;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject.MagneticTarget;
@@ -59,7 +61,7 @@
private static final float EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT = 0.1f;
private static final float EXPANDED_VIEW_ANIMATE_OUT_SCALE_AMOUNT = .75f;
- private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150;
+ private static final int EXPANDED_VIEW_EXPAND_ALPHA_DURATION = 150;
private static final int EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION = 400;
private static final int EXPANDED_VIEW_ANIMATE_TO_REST_DURATION = 400;
private static final int EXPANDED_VIEW_DISMISS_DURATION = 250;
@@ -72,6 +74,17 @@
private static final float DISMISS_VIEW_SCALE = 1.25f;
private static final int HANDLE_ALPHA_ANIMATION_DURATION = 100;
+ private static final float SWITCH_OUT_SCALE = 0.97f;
+ private static final long SWITCH_OUT_SCALE_DURATION = 200L;
+ private static final long SWITCH_OUT_ALPHA_DURATION = 100L;
+ private static final long SWITCH_OUT_HANDLE_ALPHA_DURATION = 50L;
+ private static final long SWITCH_IN_ANIM_DELAY = 50L;
+ private static final long SWITCH_IN_TX_DURATION = 350L;
+ private static final long SWITCH_IN_ALPHA_DURATION = 50L;
+ // Keep this handle alpha delay at least as long as alpha animation for both expanded views.
+ private static final long SWITCH_IN_HANDLE_ALPHA_DELAY = 150L;
+ private static final long SWITCH_IN_HANDLE_ALPHA_DURATION = 100L;
+
/** Spring config for the expanded view scale-in animation. */
private final PhysicsAnimator.SpringConfig mScaleInSpringConfig =
new PhysicsAnimator.SpringConfig(300f, 0.9f);
@@ -80,68 +93,24 @@
private final PhysicsAnimator.SpringConfig mScaleOutSpringConfig =
new PhysicsAnimator.SpringConfig(900f, 1f);
+ private final int mSwitchAnimPositionOffset;
+
/** Matrix used to scale the expanded view container with a given pivot point. */
private final AnimatableScaleMatrix mExpandedViewContainerMatrix = new AnimatableScaleMatrix();
- /** Animator for animating the expanded view's alpha (including the TaskView inside it). */
- private final ValueAnimator mExpandedViewAlphaAnimator = ValueAnimator.ofFloat(0f, 1f);
-
@Nullable
- private Animator mRunningDragAnimator;
+ private Animator mRunningAnimator;
- private final Context mContext;
- private final BubbleBarLayerView mLayerView;
private final BubblePositioner mPositioner;
private final int[] mTmpLocation = new int[2];
+ // TODO(b/381936992): remove expanded bubble state from this helper class
private BubbleViewProvider mExpandedBubble;
- private boolean mIsExpanded = false;
- public BubbleBarAnimationHelper(Context context,
- BubbleBarLayerView bubbleBarLayerView,
- BubblePositioner positioner) {
- mContext = context;
- mLayerView = bubbleBarLayerView;
+ public BubbleBarAnimationHelper(Context context, BubblePositioner positioner) {
mPositioner = positioner;
-
- mExpandedViewAlphaAnimator.setDuration(EXPANDED_VIEW_ALPHA_ANIMATION_DURATION);
- mExpandedViewAlphaAnimator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
- mExpandedViewAlphaAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- BubbleBarExpandedView bbev = getExpandedView();
- if (bbev != null) {
- // We need to be Z ordered on top in order for alpha animations to work.
- bbev.setSurfaceZOrderedOnTop(true);
- bbev.setAnimating(true);
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- BubbleBarExpandedView bbev = getExpandedView();
- if (bbev != null) {
- // The surface needs to be Z ordered on top for alpha values to work on the
- // TaskView, and if we're temporarily hidden, we are still on the screen
- // with alpha = 0f until we animate back. Stay Z ordered on top so the alpha
- // = 0f remains in effect.
- if (mIsExpanded) {
- bbev.setSurfaceZOrderedOnTop(false);
- }
-
- bbev.setContentVisibility(mIsExpanded);
- bbev.setAnimating(false);
- }
- }
- });
- mExpandedViewAlphaAnimator.addUpdateListener(valueAnimator -> {
- BubbleBarExpandedView bbev = getExpandedView();
- if (bbev != null) {
- float alpha = (float) valueAnimator.getAnimatedValue();
- bbev.setTaskViewAlpha(alpha);
- bbev.setAlpha(alpha);
- }
- });
+ mSwitchAnimPositionOffset = context.getResources().getDimensionPixelSize(
+ R.dimen.bubble_bar_expanded_view_switch_offset);
}
/**
@@ -154,18 +123,11 @@
if (bbev == null) {
return;
}
- mIsExpanded = true;
mExpandedViewContainerMatrix.setScaleX(0f);
mExpandedViewContainerMatrix.setScaleY(0f);
- updateExpandedView();
- bbev.setAnimating(true);
- bbev.setSurfaceZOrderedOnTop(true);
- bbev.setContentVisibility(false);
- bbev.setAlpha(0f);
- bbev.setTaskViewAlpha(0f);
- bbev.setVisibility(VISIBLE);
+ prepareForAnimateIn(bbev);
setScaleFromBubbleBar(mExpandedViewContainerMatrix,
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT);
@@ -173,7 +135,16 @@
bbev.setAnimationMatrix(mExpandedViewContainerMatrix);
bbev.animateExpansionWhenTaskViewVisible(() -> {
- mExpandedViewAlphaAnimator.start();
+ ObjectAnimator alphaAnim = createAlphaAnimator(bbev, /* visible= */ true);
+ alphaAnim.setDuration(EXPANDED_VIEW_EXPAND_ALPHA_DURATION);
+ alphaAnim.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
+ alphaAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ bbev.setAnimating(false);
+ }
+ });
+ startNewAnimator(alphaAnim);
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
@@ -188,7 +159,7 @@
})
.withEndActions(() -> {
bbev.setAnimationMatrix(null);
- updateExpandedView();
+ updateExpandedView(bbev);
if (afterAnimation != null) {
afterAnimation.run();
}
@@ -197,13 +168,24 @@
});
}
+ private void prepareForAnimateIn(BubbleBarExpandedView bbev) {
+ bbev.setAnimating(true);
+ updateExpandedView(bbev);
+ // We need to be Z ordered on top in order for taskView alpha to work.
+ // It is also set when the alpha animation starts, but needs to be set here to too avoid
+ // flickers.
+ bbev.setSurfaceZOrderedOnTop(true);
+ bbev.setTaskViewAlpha(0f);
+ bbev.setContentVisibility(false);
+ bbev.setVisibility(VISIBLE);
+ }
+
/**
* Collapses the currently expanded bubble.
*
* @param endRunnable a runnable to run at the end of the animation.
*/
public void animateCollapse(Runnable endRunnable) {
- mIsExpanded = false;
final BubbleBarExpandedView bbev = getExpandedView();
if (bbev == null) {
Log.w(TAG, "Trying to animate collapse without a bubble");
@@ -214,6 +196,19 @@
setScaleFromBubbleBar(mExpandedViewContainerMatrix, 1f);
+ bbev.setAnimating(true);
+
+ ObjectAnimator alphaAnim = createAlphaAnimator(bbev, /* visible= */ false);
+ alphaAnim.setDuration(EXPANDED_VIEW_EXPAND_ALPHA_DURATION);
+ alphaAnim.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
+ alphaAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ bbev.setAnimating(false);
+ }
+ });
+ startNewAnimator(alphaAnim);
+
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
.spring(AnimatableScaleMatrix.SCALE_X,
@@ -234,7 +229,6 @@
}
})
.start();
- mExpandedViewAlphaAnimator.reverse();
}
private void setScaleFromBubbleBar(AnimatableScaleMatrix matrix, float scale) {
@@ -246,6 +240,143 @@
}
/**
+ * Animate between two bubble views using a switch animation
+ *
+ * @param fromBubble bubble to hide
+ * @param toBubble bubble to show
+ * @param afterAnimation optional runnable after animation finishes
+ */
+ public void animateSwitch(BubbleViewProvider fromBubble, BubbleViewProvider toBubble,
+ @Nullable Runnable afterAnimation) {
+ /*
+ * Switch animation
+ *
+ * |.....................fromBubble scale to 0.97.....................|
+ * |fromBubble handle alpha 0|....fromBubble alpha to 0.....| |
+ * 0-------------------------50-----------------------100---150--------200----------250--400
+ * |..toBubble alpha to 1...| |toBubble handle alpha 1| |
+ * |................toBubble position +/-48 to 0...............|
+ */
+
+ mExpandedBubble = toBubble;
+ final BubbleBarExpandedView toBbev = toBubble.getBubbleBarExpandedView();
+ final BubbleBarExpandedView fromBbev = fromBubble.getBubbleBarExpandedView();
+ if (toBbev == null || fromBbev == null) {
+ return;
+ }
+
+ fromBbev.setAnimating(true);
+
+ prepareForAnimateIn(toBbev);
+ final float endTx = toBbev.getTranslationX();
+ final float startTx = getSwitchAnimationInitialTx(endTx);
+ toBbev.setTranslationX(startTx);
+ toBbev.getHandleView().setAlpha(0f);
+ toBbev.getHandleView().setHandleInitialColor(fromBbev.getHandleView().getHandleColor());
+
+ toBbev.animateExpansionWhenTaskViewVisible(() -> {
+ AnimatorSet switchAnim = new AnimatorSet();
+ switchAnim.playTogether(switchOutAnimator(fromBbev), switchInAnimator(toBbev, endTx));
+
+ switchAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (afterAnimation != null) {
+ afterAnimation.run();
+ }
+ }
+ });
+ startNewAnimator(switchAnim);
+ });
+ }
+
+ private float getSwitchAnimationInitialTx(float endTx) {
+ if (mPositioner.isBubbleBarOnLeft()) {
+ return endTx - mSwitchAnimPositionOffset;
+ } else {
+ return endTx + mSwitchAnimPositionOffset;
+ }
+ }
+
+ private Animator switchOutAnimator(BubbleBarExpandedView bbev) {
+ setPivotToCenter(bbev);
+ AnimatorSet scaleAnim = new AnimatorSet();
+ scaleAnim.playTogether(
+ ObjectAnimator.ofFloat(bbev, SCALE_X, SWITCH_OUT_SCALE),
+ ObjectAnimator.ofFloat(bbev, SCALE_Y, SWITCH_OUT_SCALE)
+ );
+ scaleAnim.setInterpolator(Interpolators.ACCELERATE);
+ scaleAnim.setDuration(SWITCH_OUT_SCALE_DURATION);
+
+ ObjectAnimator alphaAnim = createAlphaAnimator(bbev, /* visible= */ false);
+ alphaAnim.setStartDelay(SWITCH_OUT_HANDLE_ALPHA_DURATION);
+ alphaAnim.setDuration(SWITCH_OUT_ALPHA_DURATION);
+
+ ObjectAnimator handleAlphaAnim = ObjectAnimator.ofFloat(bbev.getHandleView(), ALPHA, 0f)
+ .setDuration(SWITCH_OUT_HANDLE_ALPHA_DURATION);
+
+ AnimatorSet animator = new AnimatorSet();
+ animator.playTogether(scaleAnim, alphaAnim, handleAlphaAnim);
+
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ bbev.setAnimating(false);
+ }
+ });
+ return animator;
+ }
+
+ private Animator switchInAnimator(BubbleBarExpandedView bbev, float restingTx) {
+ ObjectAnimator positionAnim = ObjectAnimator.ofFloat(bbev, TRANSLATION_X, restingTx);
+ positionAnim.setInterpolator(Interpolators.EMPHASIZED_DECELERATE);
+ positionAnim.setStartDelay(SWITCH_IN_ANIM_DELAY);
+ positionAnim.setDuration(SWITCH_IN_TX_DURATION);
+
+ // Animate alpha directly to have finer control over surface z-ordering
+ ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(bbev, TASK_VIEW_ALPHA, 1f);
+ alphaAnim.setStartDelay(SWITCH_IN_ANIM_DELAY);
+ alphaAnim.setDuration(SWITCH_IN_ALPHA_DURATION);
+ alphaAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ bbev.setSurfaceZOrderedOnTop(true);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ bbev.setContentVisibility(true);
+ // The outgoing expanded view alpha animation is still in progress.
+ // Do not reset the surface z-order as otherwise the outgoing expanded view is
+ // placed on top.
+ }
+ });
+
+ ObjectAnimator handleAlphaAnim = ObjectAnimator.ofFloat(bbev.getHandleView(), ALPHA, 1f);
+ handleAlphaAnim.setStartDelay(SWITCH_IN_HANDLE_ALPHA_DELAY);
+ handleAlphaAnim.setDuration(SWITCH_IN_HANDLE_ALPHA_DURATION);
+ handleAlphaAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ bbev.setSurfaceZOrderedOnTop(false);
+ bbev.setAnimating(false);
+ }
+ });
+
+ AnimatorSet animator = new AnimatorSet();
+ animator.playTogether(positionAnim, alphaAnim, handleAlphaAnim);
+
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ updateExpandedView(bbev);
+ }
+ });
+ return animator;
+ }
+
+
+ /**
* Animate the expanded bubble when it is being dragged
*/
public void animateStartDrag() {
@@ -273,7 +404,7 @@
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(contentAnim, handleAnim);
animatorSet.addListener(new DragAnimatorListenerAdapter(bbev));
- startNewDragAnimation(animatorSet);
+ startNewAnimator(animatorSet);
}
/**
@@ -282,7 +413,6 @@
* @param endRunnable a runnable to run at the end of the animation
*/
public void animateDismiss(Runnable endRunnable) {
- mIsExpanded = false;
final BubbleBarExpandedView bbev = getExpandedView();
if (bbev == null) {
Log.w(TAG, "Trying to animate dismiss without a bubble");
@@ -335,7 +465,7 @@
bbev.setDragging(false);
}
});
- startNewDragAnimation(animatorSet);
+ startNewAnimator(animatorSet);
}
/**
@@ -409,7 +539,7 @@
}
}
});
- startNewDragAnimation(animatorSet);
+ startNewAnimator(animatorSet);
}
/**
@@ -435,7 +565,7 @@
animatorSet.setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION).setInterpolator(
EMPHASIZED_DECELERATE);
animatorSet.addListener(new DragAnimatorListenerAdapter(bbev));
- startNewDragAnimation(animatorSet);
+ startNewAnimator(animatorSet);
}
/**
@@ -443,14 +573,15 @@
*/
public void cancelAnimations() {
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
- mExpandedViewAlphaAnimator.cancel();
BubbleBarExpandedView bbev = getExpandedView();
if (bbev != null) {
bbev.animate().cancel();
}
- if (mRunningDragAnimator != null) {
- mRunningDragAnimator.cancel();
- mRunningDragAnimator = null;
+ if (mRunningAnimator != null) {
+ if (mRunningAnimator.isRunning()) {
+ mRunningAnimator.cancel();
+ }
+ mRunningAnimator = null;
}
}
@@ -462,8 +593,7 @@
return null;
}
- private void updateExpandedView() {
- BubbleBarExpandedView bbev = getExpandedView();
+ private void updateExpandedView(BubbleBarExpandedView bbev) {
if (bbev == null) {
Log.w(TAG, "Trying to update the expanded view without a bubble");
return;
@@ -477,6 +607,8 @@
bbev.setLayoutParams(lp);
bbev.setX(position.x);
bbev.setY(position.y);
+ bbev.setScaleX(1f);
+ bbev.setScaleY(1f);
bbev.updateLocation();
bbev.maybeShowOverflow();
}
@@ -500,18 +632,54 @@
return new Size(width, height);
}
- private void startNewDragAnimation(Animator animator) {
+ private void startNewAnimator(Animator animator) {
cancelAnimations();
- mRunningDragAnimator = animator;
+ mRunningAnimator = animator;
animator.start();
}
+ /**
+ * Animate the alpha of the expanded view between visible (1) and invisible (0).
+ * {@link BubbleBarExpandedView} requires
+ * {@link com.android.wm.shell.bubbles.BubbleExpandedView#setSurfaceZOrderedOnTop(boolean)} to
+ * be called before alpha can be applied.
+ * Only supports alpha of 1 or 0. Otherwise we can't reset surface z-order at the end.
+ */
+ private ObjectAnimator createAlphaAnimator(BubbleBarExpandedView bubbleBarExpandedView,
+ boolean visible) {
+ ObjectAnimator animator = ObjectAnimator.ofFloat(bubbleBarExpandedView, TASK_VIEW_ALPHA,
+ visible ? 1f : 0f);
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ // Move task view to the top of the window so alpha can be applied to it
+ bubbleBarExpandedView.setSurfaceZOrderedOnTop(true);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ bubbleBarExpandedView.setContentVisibility(visible);
+ if (!visible) {
+ // Hide the expanded view before we reset the z-ordering
+ bubbleBarExpandedView.setVisibility(INVISIBLE);
+ }
+ bubbleBarExpandedView.setSurfaceZOrderedOnTop(false);
+ }
+ });
+ return animator;
+ }
+
private static void setDragPivot(BubbleBarExpandedView bbev) {
bbev.setPivotX(bbev.getWidth() / 2f);
bbev.setPivotY(0f);
}
- private class DragAnimatorListenerAdapter extends AnimatorListenerAdapter {
+ private static void setPivotToCenter(BubbleBarExpandedView bbev) {
+ bbev.setPivotX(bbev.getWidth() / 2f);
+ bbev.setPivotY(bbev.getHeight() / 2f);
+ }
+
+ private static class DragAnimatorListenerAdapter extends AnimatorListenerAdapter {
private final BubbleBarExpandedView mBubbleBarExpandedView;
@@ -523,11 +691,9 @@
public void onAnimationStart(Animator animation) {
mBubbleBarExpandedView.setAnimating(true);
}
-
@Override
public void onAnimationEnd(Animator animation) {
mBubbleBarExpandedView.setAnimating(false);
- mRunningDragAnimator = null;
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index ed49417..65c929a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -85,6 +85,22 @@
}
};
+ /**
+ * Property to set alpha for the task view
+ */
+ public static final FloatProperty<BubbleBarExpandedView> TASK_VIEW_ALPHA = new FloatProperty<>(
+ "taskViewAlpha") {
+ @Override
+ public void setValue(BubbleBarExpandedView bbev, float alpha) {
+ bbev.setTaskViewAlpha(alpha);
+ }
+
+ @Override
+ public Float get(BubbleBarExpandedView bbev) {
+ return bbev.mTaskView != null ? bbev.mTaskView.getAlpha() : bbev.getAlpha();
+ }
+ };
+
private static final String TAG = BubbleBarExpandedView.class.getSimpleName();
private static final int INVALID_TASK_ID = -1;
@@ -590,6 +606,11 @@
mTaskView.setZOrderedOnTop(onTop, true /* allowDynamicChange */);
}
+ @VisibleForTesting
+ boolean isSurfaceZOrderedOnTop() {
+ return mTaskView != null && mTaskView.isZOrderedOnTop();
+ }
+
/**
* Sets whether the view is animating, in this case we won't change the content visibility
* until the animation is done.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
index 712e41b..9cf0d2d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
@@ -65,6 +65,7 @@
@Nullable
private ObjectAnimator mColorChangeAnim;
private @ColorInt int mRegionSamplerColor;
+ private boolean mHasSampledColor;
public BubbleBarHandleView(Context context) {
this(context, null /* attrs */);
@@ -102,7 +103,11 @@
invalidate();
}
- private int getHandleColor() {
+ /**
+ * Get current color value for the handle
+ */
+ @ColorInt
+ public int getHandleColor() {
return mHandlePaint.getColor();
}
@@ -128,6 +133,16 @@
}
/**
+ * Set initial color for the handle. Takes effect if the
+ * {@link #updateHandleColor(boolean, boolean)} has not been called.
+ */
+ public void setHandleInitialColor(@ColorInt int color) {
+ if (!mHasSampledColor) {
+ setHandleColor(color);
+ }
+ }
+
+ /**
* Updates the handle color.
*
* @param isRegionDark Whether the background behind the handle is dark, and thus the handle
@@ -139,6 +154,7 @@
if (newColor == mRegionSamplerColor) {
return;
}
+ mHasSampledColor = true;
mRegionSamplerColor = newColor;
if (mColorChangeAnim != null) {
mColorChangeAnim.cancel();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 0c05e3c..425afbe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -103,8 +103,7 @@
mPositioner = mBubbleController.getPositioner();
mBubbleLogger = bubbleLogger;
- mAnimationHelper = new BubbleBarAnimationHelper(context,
- this, mPositioner);
+ mAnimationHelper = new BubbleBarAnimationHelper(context, mPositioner);
mEducationViewController = new BubbleEducationViewController(context, (boolean visible) -> {
if (mExpandedView == null) return;
mExpandedView.setObscured(visible);
@@ -183,8 +182,14 @@
// Already showing this bubble, skip animating
return;
}
+ BubbleViewProvider previousBubble = null;
if (mExpandedBubble != null && !b.getKey().equals(mExpandedBubble.getKey())) {
- removeView(mExpandedView);
+ if (mIsExpanded) {
+ // Previous expanded view open, keep it visible to animate the switch
+ previousBubble = mExpandedBubble;
+ } else {
+ removeView(mExpandedView);
+ }
mExpandedView = null;
}
if (mExpandedView == null) {
@@ -246,7 +251,8 @@
mIsExpanded = true;
mBubbleController.getSysuiProxy().onStackExpandChanged(true);
- mAnimationHelper.animateExpansion(mExpandedBubble, () -> {
+
+ final Runnable afterAnimation = () -> {
if (mExpandedView == null) return;
// Touch delegate for the menu
BubbleBarHandleView view = mExpandedView.getHandleView();
@@ -256,7 +262,18 @@
mHandleTouchDelegate = new TouchDelegate(mHandleTouchBounds,
mExpandedView.getHandleView());
setTouchDelegate(mHandleTouchDelegate);
- });
+ };
+
+ if (previousBubble != null) {
+ final BubbleBarExpandedView previousExpandedView =
+ previousBubble.getBubbleBarExpandedView();
+ mAnimationHelper.animateSwitch(previousBubble, mExpandedBubble, () -> {
+ removeView(previousExpandedView);
+ afterAnimation.run();
+ });
+ } else {
+ mAnimationHelper.animateExpansion(mExpandedBubble, afterAnimation);
+ }
showScrim(true);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/BoostExecutor.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/BoostExecutor.kt
new file mode 100644
index 0000000..498d0e4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/BoostExecutor.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.wm.shell.common
+
+import android.os.Looper
+import java.util.concurrent.Executor
+
+/** Executor implementation which can be boosted temporarily to a different thread priority. */
+interface BoostExecutor : Executor {
+ /**
+ * Requests that the executor is boosted until {@link #resetBoost()} is called.
+ */
+ fun setBoost() {}
+
+ /**
+ * Requests that the executor is not boosted (only resets if there are no other boost requests
+ * in progress).
+ */
+ fun resetBoost() {}
+
+ /**
+ * Returns whether the executor is boosted.
+ */
+ fun isBoosted() : Boolean {
+ return false
+ }
+
+ /**
+ * Returns the looper for this executor.
+ */
+ fun getLooper() : Looper? {
+ return Looper.myLooper()
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 38b8592..ec3c0b8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -228,7 +228,6 @@
public class PerDisplay implements DisplayInsetsController.OnInsetsChangedListener {
final int mDisplayId;
final InsetsState mInsetsState = new InsetsState();
- @InsetsType int mRequestedVisibleTypes = WindowInsets.Type.defaultVisible();
boolean mImeRequestedVisible =
(WindowInsets.Type.defaultVisible() & WindowInsets.Type.ime()) != 0;
InsetsSourceControl mImeSourceControl = null;
@@ -426,12 +425,10 @@
*/
private void setVisibleDirectly(boolean visible, @Nullable ImeTracker.Token statsToken) {
mInsetsState.setSourceVisible(InsetsSource.ID_IME, visible);
- mRequestedVisibleTypes = visible
- ? mRequestedVisibleTypes | WindowInsets.Type.ime()
- : mRequestedVisibleTypes & ~WindowInsets.Type.ime();
+ int visibleTypes = visible ? WindowInsets.Type.ime() : 0;
try {
mWmService.updateDisplayWindowRequestedVisibleTypes(mDisplayId,
- mRequestedVisibleTypes, statsToken);
+ visibleTypes, WindowInsets.Type.ime(), statsToken);
} catch (RemoteException e) {
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
index 736d954..803f16c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
@@ -16,15 +16,50 @@
package com.android.wm.shell.common;
+import static android.os.Process.THREAD_PRIORITY_DEFAULT;
+import static android.os.Process.setThreadPriority;
+
import android.annotation.NonNull;
import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.util.function.BiConsumer;
/** Executor implementation which is backed by a Handler. */
public class HandlerExecutor implements ShellExecutor {
+ @NonNull
private final Handler mHandler;
+ // See android.os.Process#THREAD_PRIORITY_*
+ private final int mDefaultThreadPriority;
+ private final int mBoostedThreadPriority;
+ // Number of current requests to boost thread priority
+ private int mBoostCount;
+ private final Object mBoostLock = new Object();
+ // Default function for setting thread priority (tid, priority)
+ private BiConsumer<Integer, Integer> mSetThreadPriorityFn =
+ HandlerExecutor::setThreadPriorityInternal;
public HandlerExecutor(@NonNull Handler handler) {
+ this(handler, THREAD_PRIORITY_DEFAULT, THREAD_PRIORITY_DEFAULT);
+ }
+
+ /**
+ * Used only if this executor can be boosted, if so, it can be boosted to the given
+ * {@param boostPriority}.
+ */
+ public HandlerExecutor(@NonNull Handler handler, int defaultThreadPriority,
+ int boostedThreadPriority) {
mHandler = handler;
+ mDefaultThreadPriority = defaultThreadPriority;
+ mBoostedThreadPriority = boostedThreadPriority;
+ }
+
+ @VisibleForTesting
+ void replaceSetThreadPriorityFn(BiConsumer<Integer, Integer> setThreadPriorityFn) {
+ mSetThreadPriorityFn = setThreadPriorityFn;
}
@Override
@@ -56,9 +91,54 @@
}
@Override
+ public void setBoost() {
+ synchronized (mBoostLock) {
+ if (mDefaultThreadPriority == mBoostedThreadPriority) {
+ // Nothing to boost
+ return;
+ }
+ if (mBoostCount == 0) {
+ mSetThreadPriorityFn.accept(
+ ((HandlerThread) mHandler.getLooper().getThread()).getThreadId(),
+ mBoostedThreadPriority);
+ }
+ mBoostCount++;
+ }
+ }
+
+ @Override
+ public void resetBoost() {
+ synchronized (mBoostLock) {
+ mBoostCount--;
+ if (mBoostCount == 0) {
+ mSetThreadPriorityFn.accept(
+ ((HandlerThread) mHandler.getLooper().getThread()).getThreadId(),
+ mDefaultThreadPriority);
+ }
+ }
+ }
+
+ @Override
+ public boolean isBoosted() {
+ synchronized (mBoostLock) {
+ return mBoostCount > 0;
+ }
+ }
+
+ @Override
+ @NonNull
+ public Looper getLooper() {
+ return mHandler.getLooper();
+ }
+
+ @Override
public void assertCurrentThread() {
if (!mHandler.getLooper().isCurrentThread()) {
throw new IllegalStateException("must be called on " + mHandler);
}
}
+
+ private static void setThreadPriorityInternal(Integer tid, Integer priority) {
+ setThreadPriority(tid, priority);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
index 2c2961f..9e5071e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
@@ -18,15 +18,15 @@
import java.lang.reflect.Array;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
/**
* Super basic Executor interface that adds support for delayed execution and removing callbacks.
- * Intended to wrap Handler while better-supporting testing.
+ * Intended to wrap Handler while better-supporting testing. Not every ShellExecutor implementation
+ * may support boosting.
*/
-public interface ShellExecutor extends Executor {
+public interface ShellExecutor extends BoostExecutor {
/**
* Executes the given runnable. If the caller is running on the same looper as this executor,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt
index 62d5098..bc56637 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt
@@ -30,7 +30,7 @@
* desktop windowing environment.
*/
fun isTopActivityExemptFromDesktopWindowing(context: Context, task: TaskInfo) =
- (isSystemUiTask(context, task) || (task.isTopActivityTransparent && task.numActivities == 1))
+ (isSystemUiTask(context, task) || (task.numActivities > 0 && task.isActivityStackTransparent))
&& !task.isTopActivityNoDisplay
private fun isSystemUiTask(context: Context, task: TaskInfo): Boolean {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxCommandHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxCommandHandler.kt
index 819b110..2d0a3f5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxCommandHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxCommandHandler.kt
@@ -67,8 +67,8 @@
return false
}
return when (args.size) {
- 1 -> onShellDisplayCommand(args[0], pw)
- 2 -> onShellUpdateCommand(args[0], args[1], pw)
+ 1 -> onNoParamsCommand(args[0], pw)
+ 2 -> onSingleParamCommand(args[0], args[1], pw)
else -> {
pw.println("Invalid command: " + args[0])
return false
@@ -89,11 +89,17 @@
$prefix name, for example, @android:color/system_accent2_50.
$prefix backgroundColorReset"
$prefix Resets the background color to the default value."
+ $prefix cornerRadius"
+ $prefix Corners radius (in pixels) for activities in the letterbox mode."
+ $prefix If cornerRadius < 0, it will be ignored and corners of the"
+ $prefix activity won't be rounded."
+ $prefix cornerRadiusReset"
+ $prefix Resets the rounded corners radius to the default value."
""".trimIndent()
)
}
- private fun onShellUpdateCommand(command: String, value: String, pw: PrintWriter): Boolean {
+ private fun onSingleParamCommand(command: String, value: String, pw: PrintWriter): Boolean {
when (command) {
"backgroundColor" -> {
return invokeWhenValid(
@@ -120,10 +126,17 @@
}
)
- "backgroundColorReset" -> {
- letterboxConfiguration.resetLetterboxBackgroundColor()
- return true
- }
+ "cornerRadius" -> return invokeWhenValid(
+ pw,
+ value,
+ ::strToInt{ it >= 0 },
+ { radius ->
+ letterboxConfiguration.setLetterboxActivityCornersRadius(radius)
+ },
+ { r ->
+ "$r is not a valid radius. It must be an integer >= 0."
+ }
+ )
else -> {
pw.println("Invalid command: $value")
@@ -132,7 +145,7 @@
}
}
- private fun onShellDisplayCommand(command: String, pw: PrintWriter): Boolean {
+ private fun onNoParamsCommand(command: String, pw: PrintWriter): Boolean {
when (command) {
"backgroundColor" -> {
pw.println(
@@ -144,6 +157,24 @@
return true
}
+ "backgroundColorReset" -> {
+ letterboxConfiguration.resetLetterboxBackgroundColor()
+ return true
+ }
+
+ "cornerRadius" -> {
+ pw.println(
+ " Rounded corners radius: " +
+ "${letterboxConfiguration.getLetterboxActivityCornersRadius()} px."
+ )
+ return true
+ }
+
+ "cornerRadiusReset" -> {
+ letterboxConfiguration.resetLetterboxActivityCornersRadius()
+ return true
+ }
+
else -> {
pw.println("Invalid command: $command")
return false
@@ -181,4 +212,15 @@
} catch (e: IllegalArgumentException) {
null
}
+
+ // Converts a String to Int which if possible or it returns null otherwise.
+ // If a predicate is set, it also returns [null] if the predicate evaluate to [false].
+ private fun strToInt(predicate: (Int) -> Boolean = { _ -> true }): (String) -> Int? = { str ->
+ try {
+ val value = str.toInt()
+ if (predicate(value)) value else null
+ } catch (e: IllegalArgumentException) {
+ null
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxConfiguration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxConfiguration.kt
index 83a8e31..244ff99 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxConfiguration.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxConfiguration.kt
@@ -36,6 +36,21 @@
// Color resource id for the solid color letterbox background type.
private var letterboxBackgroundColorResourceIdOverride: Int? = null
+ // Default value for corners radius for activities presented in the letterbox mode.
+ // Values < 0 will be ignored.
+ private val letterboxActivityDefaultCornersRadius: Int
+
+ // Current corners radius for activities presented in the letterbox mode.
+ // Values can be modified at runtime and values < 0 will be ignored.
+ private var letterboxActivityCornersRadius = 0
+
+ init {
+ letterboxActivityDefaultCornersRadius = context.resources.getInteger(
+ R.integer.config_letterboxActivityCornersRadius
+ )
+ letterboxActivityCornersRadius = letterboxActivityDefaultCornersRadius
+ }
+
/**
* Sets color of letterbox background which is used when using the solid background mode.
*/
@@ -64,7 +79,7 @@
}
// Query color dynamically because material colors extracted from wallpaper are updated
// when wallpaper is changed.
- return Color.valueOf(context.getResources().getColor(colorId!!, /* theme */null))
+ return Color.valueOf(context.getResources().getColor(colorId!!, null))
}
/**
@@ -79,4 +94,33 @@
* The background color for the Letterbox.
*/
fun getBackgroundColorRgbArray(): FloatArray = getLetterboxBackgroundColor().components
+
+ /**
+ * Overrides corners radius for activities presented in the letterbox mode. Values < 0,
+ * will be ignored and corners of the activity won't be rounded.
+ */
+ fun setLetterboxActivityCornersRadius(cornersRadius: Int) {
+ letterboxActivityCornersRadius = cornersRadius
+ }
+
+ /**
+ * Resets corners radius for activities presented in the letterbox mode.
+ */
+ fun resetLetterboxActivityCornersRadius() {
+ letterboxActivityCornersRadius = letterboxActivityDefaultCornersRadius
+ }
+
+ /**
+ * Whether corners of letterboxed activities are rounded.
+ */
+ fun isLetterboxActivityCornersRounded(): Boolean {
+ return getLetterboxActivityCornersRadius() > 0
+ }
+
+ /**
+ * Gets corners radius for activities presented in the letterbox mode.
+ */
+ fun getLetterboxActivityCornersRadius(): Int {
+ return maxOf(letterboxActivityCornersRadius, 0)
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerStrategy.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerStrategy.kt
index 9e3edf6..0c3769e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerStrategy.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerStrategy.kt
@@ -16,6 +16,8 @@
package com.android.wm.shell.compatui.letterbox
+import com.android.wm.shell.compatui.letterbox.LetterboxControllerStrategy.LetterboxMode.MULTIPLE_SURFACES
+import com.android.wm.shell.compatui.letterbox.LetterboxControllerStrategy.LetterboxMode.SINGLE_SURFACE
import com.android.wm.shell.dagger.WMSingleton
import javax.inject.Inject
@@ -24,24 +26,28 @@
* implementing letterbox in shell.
*/
@WMSingleton
-class LetterboxControllerStrategy @Inject constructor() {
+class LetterboxControllerStrategy @Inject constructor(
+ private val letterboxConfiguration: LetterboxConfiguration
+) {
// Different letterbox implementation modes.
enum class LetterboxMode { SINGLE_SURFACE, MULTIPLE_SURFACES }
@Volatile
- private var currentMode: LetterboxMode = LetterboxMode.SINGLE_SURFACE
+ private var currentMode: LetterboxMode = SINGLE_SURFACE
fun configureLetterboxMode() {
// TODO(b/377875146): Define criteria for switching between [LetterboxMode]s.
- currentMode = if (android.os.SystemProperties.getInt(
- "multi_interface",
- 0
- ) == 0
- ) {
- LetterboxMode.SINGLE_SURFACE
+ // At the moment, we use the presence of rounded corners to understand if to use a single
+ // surface or multiple surfaces for the letterbox areas. This rule will change when
+ // considering transparent activities which won't have rounded corners leading to the
+ // [MULTIPLE_SURFACES] option.
+ // The chosen strategy will depend on performance considerations,
+ // including surface memory usage and the impact of the rounded corners solution.
+ currentMode = if (letterboxConfiguration.isLetterboxActivityCornersRounded()) {
+ SINGLE_SURFACE
} else {
- LetterboxMode.MULTIPLE_SURFACES
+ MULTIPLE_SURFACES
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxUtils.kt
index ef964f4..8e149ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxUtils.kt
@@ -66,3 +66,41 @@
other.dump()
}
}
+
+object LetterboxUtils {
+ // Utility methods about Maps usage in Letterbox.
+ object Maps {
+ /*
+ * Executes [onFound] on the [item] for a given [key] if present or
+ * [onMissed] if the [key] is not present.
+ */
+ fun <V, K> MutableMap<K, V>.runOnItem(
+ key: K,
+ onFound: (V) -> Unit = { _ -> },
+ onMissed: (
+ K,
+ MutableMap<K, V>
+ ) -> Unit = { _, _ -> }
+ ) {
+ this[key]?.let {
+ return onFound(it)
+ }
+ return onMissed(key, this)
+ }
+ }
+
+ // Utility methods about Transaction usage in Letterbox.
+ object Transactions {
+ // Sets position and crops in one method.
+ fun Transaction.moveAndCrop(
+ surface: SurfaceControl,
+ rect: Rect
+ ): Transaction =
+ setPosition(surface, rect.left.toFloat(), rect.top.toFloat())
+ .setWindowCrop(
+ surface,
+ rect.width(),
+ rect.height()
+ )
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxController.kt
index 5129d03..6861aa3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxController.kt
@@ -20,6 +20,8 @@
import android.view.SurfaceControl
import android.view.SurfaceControl.Transaction
import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.compatui.letterbox.LetterboxUtils.Maps.runOnItem
+import com.android.wm.shell.compatui.letterbox.LetterboxUtils.Transactions.moveAndCrop
import com.android.wm.shell.dagger.WMSingleton
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT
import javax.inject.Inject
@@ -101,23 +103,6 @@
ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, "${letterboxMap.keys}")
}
- /*
- * Executes [onFound] on the [LetterboxItem] if present or [onMissed] if not present.
- */
- private fun MutableMap<LetterboxKey, LetterboxSurfaces>.runOnItem(
- key: LetterboxKey,
- onFound: (LetterboxSurfaces) -> Unit = { _ -> },
- onMissed: (
- LetterboxKey,
- MutableMap<LetterboxKey, LetterboxSurfaces>
- ) -> Unit = { _, _ -> }
- ) {
- this[key]?.let {
- return onFound(it)
- }
- return onMissed(key, this)
- }
-
private fun SurfaceControl?.remove(
tx: Transaction
) = this?.let {
@@ -131,17 +116,6 @@
tx.setVisibility(this, visible)
}
- private fun Transaction.moveAndCrop(
- surface: SurfaceControl,
- rect: Rect
- ): Transaction =
- setPosition(surface, rect.left.toFloat(), rect.top.toFloat())
- .setWindowCrop(
- surface,
- rect.width(),
- rect.height()
- )
-
private fun LetterboxSurfaces.updateSurfacesBounds(
tx: Transaction,
taskBounds: Rect,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt
index a67f608..8e1cdee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt
@@ -20,6 +20,8 @@
import android.view.SurfaceControl
import android.view.SurfaceControl.Transaction
import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.compatui.letterbox.LetterboxUtils.Maps.runOnItem
+import com.android.wm.shell.compatui.letterbox.LetterboxUtils.Transactions.moveAndCrop
import com.android.wm.shell.dagger.WMSingleton
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT
import javax.inject.Inject
@@ -106,32 +108,4 @@
override fun dump() {
ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, "${letterboxMap.keys}")
}
-
- /*
- * Executes [onFound] on the [SurfaceControl] if present or [onMissed] if not present.
- */
- private fun MutableMap<LetterboxKey, SurfaceControl>.runOnItem(
- key: LetterboxKey,
- onFound: (SurfaceControl) -> Unit = { _ -> },
- onMissed: (
- LetterboxKey,
- MutableMap<LetterboxKey, SurfaceControl>
- ) -> Unit = { _, _ -> }
- ) {
- this[key]?.let {
- return onFound(it)
- }
- return onMissed(key, this)
- }
-
- private fun Transaction.moveAndCrop(
- surface: SurfaceControl,
- rect: Rect
- ): Transaction =
- setPosition(surface, rect.left.toFloat(), rect.top.toFloat())
- .setWindowCrop(
- surface,
- rect.width(),
- rect.height()
- )
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/HasWMComponent.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/HasWMComponent.kt
new file mode 100644
index 0000000..d5e0240
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/HasWMComponent.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dagger
+
+/**
+ * An interface implemented by the application that uses [WMComponent].
+ *
+ * This exposes the component to allow classes to do member injection for bindings where constructor
+ * injection is not possible, e.g. views.
+ */
+interface HasWMComponent {
+ fun getWMComponent(): WMComponent
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/LetterboxModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/LetterboxModule.java
new file mode 100644
index 0000000..76279ec
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/LetterboxModule.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package com.android.wm.shell.dagger;
+
+import android.annotation.NonNull;
+
+import com.android.wm.shell.common.transition.TransitionStateHolder;
+import com.android.wm.shell.compatui.letterbox.LetterboxController;
+import com.android.wm.shell.compatui.letterbox.LetterboxControllerStrategy;
+import com.android.wm.shell.compatui.letterbox.LetterboxTransitionObserver;
+import com.android.wm.shell.compatui.letterbox.MixedLetterboxController;
+import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Provides Letterbox Shell implementation components to Dagger dependency Graph.
+ */
+@Module
+public abstract class LetterboxModule {
+
+ @WMSingleton
+ @Provides
+ static LetterboxTransitionObserver provideLetterboxTransitionObserver(
+ @NonNull ShellInit shellInit,
+ @NonNull Transitions transitions,
+ @NonNull LetterboxController letterboxController,
+ @NonNull TransitionStateHolder transitionStateHolder,
+ @NonNull LetterboxControllerStrategy letterboxControllerStrategy
+ ) {
+ return new LetterboxTransitionObserver(shellInit, transitions, letterboxController,
+ transitionStateHolder, letterboxControllerStrategy);
+ }
+
+ @WMSingleton
+ @Binds
+ abstract LetterboxController bindsLetterboxController(
+ MixedLetterboxController letterboxController);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMComponent.java
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMComponent.java
index a3cdb2e..c493aad 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMComponent.java
@@ -14,17 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.dagger;
+package com.android.wm.shell.dagger;
import android.os.HandlerThread;
import androidx.annotation.Nullable;
-import com.android.systemui.SystemUIInitializer;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.bubbles.Bubbles;
-import com.android.wm.shell.dagger.WMShellModule;
-import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
import com.android.wm.shell.keyguard.KeyguardTransitions;
@@ -45,12 +42,11 @@
/**
* Dagger Subcomponent for WindowManager. This class explicitly describes the interfaces exported
- * from the WM component into the SysUI component (in
- * {@link SystemUIInitializer#init(boolean)}), and references the specific dependencies
+ * from the WM component into the SysUI component, and references the specific dependencies
* provided by its particular device/form-factor SystemUI implementation.
*
- * ie. {@link WMComponent} includes {@link WMShellModule}
- * and {@code TvWMComponent} includes {@link com.android.wm.shell.dagger.TvWMShellModule}
+ * <p> ie. {@link WMComponent} includes {@link WMShellModule} and {@code TvWMComponent} includes
+ * {@link TvWMShellModule}
*/
@WMSingleton
@Subcomponent(modules = {WMShellModule.class})
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
index c5644a8..d7ddbde 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
@@ -18,6 +18,7 @@
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static android.os.Process.THREAD_PRIORITY_DISPLAY;
+import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST;
import android.content.Context;
@@ -205,13 +206,14 @@
}
/**
- * Provides a Shell background thread Executor for low priority background tasks.
+ * Provides a Shell background thread Executor for low priority background tasks. The thread
+ * may also be boosted to THREAD_PRIORITY_FOREGROUND if necessary.
*/
@WMSingleton
@Provides
@ShellBackgroundThread
public static ShellExecutor provideSharedBackgroundExecutor(
@ShellBackgroundThread Handler handler) {
- return new HandlerExecutor(handler);
+ return new HandlerExecutor(handler, THREAD_PRIORITY_BACKGROUND, THREAD_PRIORITY_FOREGROUND);
}
}
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 806be8c..0cd0f4a 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
@@ -71,12 +71,8 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.split.SplitState;
-import com.android.wm.shell.common.transition.TransitionStateHolder;
import com.android.wm.shell.compatui.letterbox.LetterboxCommandHandler;
-import com.android.wm.shell.compatui.letterbox.LetterboxController;
-import com.android.wm.shell.compatui.letterbox.LetterboxControllerStrategy;
import com.android.wm.shell.compatui.letterbox.LetterboxTransitionObserver;
-import com.android.wm.shell.compatui.letterbox.MixedLetterboxController;
import com.android.wm.shell.dagger.back.ShellBackAnimationModule;
import com.android.wm.shell.dagger.pip.PipModule;
import com.android.wm.shell.desktopmode.CloseDesktopTaskTransitionHandler;
@@ -156,6 +152,8 @@
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer;
import com.android.wm.shell.windowdecor.common.viewhost.DefaultWindowDecorViewHostSupplier;
+import com.android.wm.shell.windowdecor.common.viewhost.PooledWindowDecorViewHostSupplier;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationPromoController;
import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController;
@@ -182,7 +180,8 @@
* <p>This module only defines Shell dependencies for handheld SystemUI implementation. Common
* dependencies should go into {@link WMShellBaseModule}.
*/
-@Module(includes = {WMShellBaseModule.class, PipModule.class, ShellBackAnimationModule.class})
+@Module(includes = {WMShellBaseModule.class, PipModule.class, ShellBackAnimationModule.class,
+ LetterboxModule.class})
public abstract class WMShellModule {
//
@@ -304,6 +303,7 @@
Transitions transitions,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
FocusTransitionObserver focusTransitionObserver,
+ WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier,
Optional<DesktopModeWindowDecorViewModel> desktopModeWindowDecorViewModel) {
if (desktopModeWindowDecorViewModel.isPresent()) {
return desktopModeWindowDecorViewModel.get();
@@ -321,7 +321,8 @@
rootTaskDisplayAreaOrganizer,
syncQueue,
transitions,
- focusTransitionObserver);
+ focusTransitionObserver,
+ windowDecorViewHostSupplier);
}
@WMSingleton
@@ -346,8 +347,16 @@
@WMSingleton
@Provides
- static WindowDecorViewHostSupplier provideWindowDecorViewHostSupplier(
- @ShellMainThread @NonNull CoroutineScope mainScope) {
+ static WindowDecorViewHostSupplier<WindowDecorViewHost> provideWindowDecorViewHostSupplier(
+ @NonNull Context context,
+ @ShellMainThread @NonNull CoroutineScope mainScope,
+ @NonNull ShellInit shellInit) {
+ final int poolSize = DesktopModeStatus.getWindowDecorScvhPoolSize(context);
+ final int preWarmSize = DesktopModeStatus.getWindowDecorPreWarmSize();
+ if (DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context) && poolSize > 0) {
+ return new PooledWindowDecorViewHostSupplier(
+ context, mainScope, shellInit, poolSize, preWarmSize);
+ }
return new DefaultWindowDecorViewHostSupplier(mainScope);
}
@@ -911,6 +920,7 @@
InteractionJankMonitor interactionJankMonitor,
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
+ WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier,
MultiInstanceHelper multiInstanceHelper,
Optional<DesktopTasksLimiter> desktopTasksLimiter,
AppHandleEducationController appHandleEducationController,
@@ -930,8 +940,8 @@
displayInsetsController, syncQueue, transitions, desktopTasksController,
desktopImmersiveController.get(),
rootTaskDisplayAreaOrganizer, interactionJankMonitor, genericLinksParser,
- assistContentRequester, multiInstanceHelper, desktopTasksLimiter,
- appHandleEducationController, appToWebEducationController,
+ assistContentRequester, windowDecorViewHostSupplier, multiInstanceHelper,
+ desktopTasksLimiter, appHandleEducationController, appToWebEducationController,
windowDecorCaptionHandleRepository, activityOrientationChangeHandler,
focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger));
}
@@ -1329,25 +1339,4 @@
return new Object();
}
- //
- // App Compat
- //
-
- @WMSingleton
- @Provides
- static LetterboxTransitionObserver provideLetterboxTransitionObserver(
- @NonNull ShellInit shellInit,
- @NonNull Transitions transitions,
- @NonNull LetterboxController letterboxController,
- @NonNull TransitionStateHolder transitionStateHolder,
- @NonNull LetterboxControllerStrategy letterboxControllerStrategy
- ) {
- return new LetterboxTransitionObserver(shellInit, transitions, letterboxController,
- transitionStateHolder, letterboxControllerStrategy);
- }
-
- @WMSingleton
- @Binds
- abstract LetterboxController bindsLetterboxController(
- MixedLetterboxController letterboxController);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
index 7764688..50187d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
@@ -77,6 +77,10 @@
override fun startMinimizedModeTransition(wct: WindowContainerTransaction?): IBinder =
freeformTaskTransitionHandler.startMinimizedModeTransition(wct)
+ /** Delegates starting PiP transition to [FreeformTaskTransitionHandler]. */
+ override fun startPipTransition(wct: WindowContainerTransaction?): IBinder =
+ freeformTaskTransitionHandler.startPipTransition(wct)
+
/** Starts close transition and handles or delegates desktop task close animation. */
override fun startRemoveTransition(wct: WindowContainerTransaction?): IBinder {
if (
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 80d8ecc..cd37113 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -53,6 +53,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.internal.policy.SystemBarUtils;
+import com.android.window.flags.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -245,9 +246,17 @@
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
final Resources resources = mContext.getResources();
final DisplayMetrics metrics = resources.getDisplayMetrics();
- final int screenWidth = metrics.widthPixels;
- final int screenHeight = metrics.heightPixels;
-
+ final int screenWidth;
+ final int screenHeight;
+ if (Flags.enableBugFixesForSecondaryDisplay()) {
+ final DisplayLayout displayLayout =
+ mDisplayController.getDisplayLayout(mTaskInfo.displayId);
+ screenWidth = displayLayout.width();
+ screenHeight = displayLayout.height();
+ } else {
+ screenWidth = metrics.widthPixels;
+ screenHeight = metrics.heightPixels;
+ }
mView = new View(mContext);
final SurfaceControl.Builder builder = new SurfaceControl.Builder();
mRootTdaOrganizer.attachToDisplayArea(mTaskInfo.displayId, builder);
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 7bb7242..0bc7ca9 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
@@ -50,6 +50,7 @@
import android.view.WindowManager.TRANSIT_CLOSE
import android.view.WindowManager.TRANSIT_NONE
import android.view.WindowManager.TRANSIT_OPEN
+import android.view.WindowManager.TRANSIT_PIP
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.widget.Toast
import android.window.DesktopModeFlags
@@ -67,7 +68,6 @@
import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE
import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE
import com.android.internal.jank.InteractionJankMonitor
-import com.android.internal.policy.ScreenDecorationsUtils
import com.android.internal.protolog.ProtoLog
import com.android.window.flags.Flags
import com.android.wm.shell.Flags.enableFlexibleSplit
@@ -221,6 +221,7 @@
// Launch cookie used to identify a drag and drop transition to fullscreen after it has begun.
// Used to prevent handleRequest from moving the new fullscreen task to freeform.
private var dragAndDropFullscreenCookie: Binder? = null
+ private var pendingPipTransitionAndTask: Pair<IBinder, Int>? = null
init {
desktopMode = DesktopModeImpl()
@@ -362,8 +363,15 @@
}
val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId)
- requireNotNull(tdaInfo) {
- "This method can only be called with the ID of a display having non-null DisplayArea."
+ // A non-organized display (e.g., non-trusted virtual displays used in CTS) doesn't have
+ // TDA.
+ if (tdaInfo == null) {
+ logW(
+ "forceEnterDesktop cannot find DisplayAreaInfo for displayId=%d. This could happen" +
+ " when the display is a non-trusted virtual display.",
+ displayId,
+ )
+ return false
}
val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
val isFreeformDisplay = tdaWindowingMode == WINDOWING_MODE_FREEFORM
@@ -558,6 +566,26 @@
}
fun minimizeTask(taskInfo: RunningTaskInfo) {
+ val wct = WindowContainerTransaction()
+
+ val isMinimizingToPip = taskInfo.pictureInPictureParams?.isAutoEnterEnabled() ?: false
+ // If task is going to PiP, start a PiP transition instead of a minimize transition
+ if (isMinimizingToPip) {
+ val requestInfo = TransitionRequestInfo(
+ TRANSIT_PIP, /* triggerTask= */ null, taskInfo, /* remoteTransition= */ null,
+ /* displayChange= */ null, /* flags= */ 0
+ )
+ val requestRes = transitions.dispatchRequest(Binder(), requestInfo, /* skip= */ null)
+ wct.merge(requestRes.second, true)
+ pendingPipTransitionAndTask =
+ freeformTaskTransitionStarter.startPipTransition(wct) to taskInfo.taskId
+ return
+ }
+
+ minimizeTaskInner(taskInfo)
+ }
+
+ private fun minimizeTaskInner(taskInfo: RunningTaskInfo) {
val taskId = taskInfo.taskId
val displayId = taskInfo.displayId
val wct = WindowContainerTransaction()
@@ -885,7 +913,10 @@
destinationBounds.height(),
displayController,
)
- toggleResizeDesktopTaskTransitionHandler.startTransition(wct)
+ toggleResizeDesktopTaskTransitionHandler.startTransition(
+ wct,
+ interaction.animationStartBounds,
+ )
}
private fun dragToMaximizeDesktopTask(
@@ -916,6 +947,7 @@
direction = ToggleTaskSizeInteraction.Direction.MAXIMIZE,
source = ToggleTaskSizeInteraction.Source.HEADER_DRAG_TO_TOP,
inputMethod = DesktopModeEventLogger.getInputMethodFromMotionEvent(motionEvent),
+ animationStartBounds = currentDragBounds,
),
)
}
@@ -1203,9 +1235,12 @@
moveHomeTask(wct, toTop = true)
// Currently, we only handle the desktop on the default display really.
- if (displayId == DEFAULT_DISPLAY && ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()) {
+ if (
+ (displayId == DEFAULT_DISPLAY || Flags.enableBugFixesForSecondaryDisplay()) &&
+ ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()
+ ) {
// Add translucent wallpaper activity to show the wallpaper underneath
- addWallpaperActivity(wct)
+ addWallpaperActivity(displayId, wct)
}
val expandedTasksOrderedFrontToBack = taskRepository.getExpandedTasksOrdered(displayId)
@@ -1254,7 +1289,7 @@
?.let { homeTask -> wct.reorder(homeTask.getToken(), /* onTop= */ toTop) }
}
- private fun addWallpaperActivity(wct: WindowContainerTransaction) {
+ private fun addWallpaperActivity(displayId: Int, wct: WindowContainerTransaction) {
logV("addWallpaperActivity")
val userHandle = UserHandle.of(userId)
val userContext = context.createContextAsUser(userHandle, /* flags= */ 0)
@@ -1265,6 +1300,9 @@
launchWindowingMode = WINDOWING_MODE_FULLSCREEN
pendingIntentBackgroundActivityStartMode =
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
+ if (Flags.enableBugFixesForSecondaryDisplay()) {
+ launchDisplayId = displayId
+ }
}
val pendingIntent =
PendingIntent.getActivityAsUser(
@@ -1330,6 +1368,21 @@
return false
}
+ override fun onTransitionConsumed(
+ transition: IBinder,
+ aborted: Boolean,
+ finishT: Transaction?
+ ) {
+ pendingPipTransitionAndTask?.let { (pipTransition, taskId) ->
+ if (transition == pipTransition) {
+ if (aborted) {
+ shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { minimizeTaskInner(it) }
+ }
+ pendingPipTransitionAndTask = null
+ }
+ }
+ }
+
override fun handleRequest(
transition: IBinder,
request: TransitionRequestInfo,
@@ -1483,7 +1536,10 @@
if (!DesktopModeStatus.useRoundedCorners()) {
return
}
- val cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
+ val cornerRadius =
+ context.resources
+ .getDimensionPixelSize(R.dimen.desktop_windowing_freeform_rounded_corner_radius)
+ .toFloat()
info.changes
.filter { it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM }
.forEach { finishTransaction.setCornerRadius(it.leash, cornerRadius) }
@@ -1508,28 +1564,20 @@
/** Open an existing instance of an app. */
fun openInstance(callingTask: RunningTaskInfo, requestedTaskId: Int) {
- val wct = WindowContainerTransaction()
- val options = createNewWindowOptions(callingTask)
- if (options.launchWindowingMode == WINDOWING_MODE_FREEFORM) {
- wct.startTask(requestedTaskId, options.toBundle())
- val taskIdToMinimize =
- bringDesktopAppsToFrontBeforeShowingNewTask(
- callingTask.displayId,
- wct,
+ if (callingTask.isFreeform) {
+ val requestedTaskInfo = shellTaskOrganizer.getRunningTaskInfo(requestedTaskId)
+ if (requestedTaskInfo?.isFreeform == true) {
+ // If requested task is an already open freeform task, just move it to front.
+ moveTaskToFront(requestedTaskId)
+ } else {
+ moveBackgroundTaskToDesktop(
requestedTaskId,
+ WindowContainerTransaction(),
+ DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON,
)
- val exitResult =
- desktopImmersiveController.exitImmersiveIfApplicable(
- wct = wct,
- displayId = callingTask.displayId,
- excludeTaskId = requestedTaskId,
- reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
- )
- val transition = transitions.startTransition(TRANSIT_OPEN, wct, null)
- taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
- addPendingAppLaunchTransition(transition, requestedTaskId, taskIdToMinimize)
- exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
+ }
} else {
+ val options = createNewWindowOptions(callingTask)
val splitPosition = splitScreenController.determineNewInstancePosition(callingTask)
splitScreenController.startTask(
requestedTaskId,
@@ -2054,7 +2102,11 @@
syncQueue,
taskInfo,
displayController,
- context,
+ if (Flags.enableBugFixesForSecondaryDisplay()) {
+ displayController.getDisplayContext(taskInfo.displayId)
+ } else {
+ context
+ },
taskSurface,
rootTaskDisplayAreaOrganizer,
dragStartState,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index 11110f1..d23c9d0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -19,7 +19,6 @@
import android.content.Intent.FILL_IN_COMPONENT
import android.graphics.PointF
import android.graphics.Rect
-import android.os.Bundle
import android.os.IBinder
import android.os.SystemClock
import android.os.SystemProperties
@@ -139,7 +138,11 @@
taskUser,
)
val wct = WindowContainerTransaction()
- wct.sendPendingIntent(pendingIntent, launchHomeIntent, Bundle())
+ // The app that is being dragged into desktop mode might cause new transitions, make this
+ // launch transient to make sure those transitions can execute in parallel and thus won't
+ // block the end-drag transition.
+ val intentOptions = ActivityOptions.makeBasic().setTransientLaunch()
+ wct.sendPendingIntent(pendingIntent, launchHomeIntent, intentOptions.toBundle())
val startTransitionToken =
transitions.startTransition(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP, wct, this)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/ToggleTaskSizeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/ToggleTaskSizeUtils.kt
index 7afd8d7..f6ebf72 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/ToggleTaskSizeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/ToggleTaskSizeUtils.kt
@@ -15,6 +15,7 @@
*/
package com.android.wm.shell.desktopmode.common
+import android.graphics.Rect
import com.android.internal.jank.Cuj
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
@@ -23,10 +24,13 @@
import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction.Source
/** Represents a user interaction to toggle a desktop task's size from to maximize or vice versa. */
-data class ToggleTaskSizeInteraction(
+data class ToggleTaskSizeInteraction
+@JvmOverloads
+constructor(
val direction: Direction,
val source: Source,
val inputMethod: InputMethod,
+ val animationStartBounds: Rect? = null,
) {
constructor(
isMaximized: Boolean,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md
index 9d01535..837a6dd3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md
@@ -36,7 +36,8 @@
thread)
- This is always another thread even if config_enableShellMainThread is not set true
- **Note**:
- - This thread runs with `THREAD_PRIORITY_BACKGROUND` priority
+ - This thread runs with `THREAD_PRIORITY_BACKGROUND` priority but can be requested to be boosted
+ to `THREAD_PRIORITY_FOREGROUND`
- `ShellAnimationThread` (currently only used for Transitions and Splitscreen, but potentially all
animations could be offloaded here)
- `ShellSplashScreenThread` (only for use with splashscreens)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
index 2ae9828..52b6c62 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowManager.TRANSIT_PIP;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -99,6 +100,12 @@
return token;
}
+ @Override
+ public IBinder startPipTransition(WindowContainerTransaction wct) {
+ final IBinder token = mTransitions.startTransition(TRANSIT_PIP, wct, null);
+ mPendingTransitionTokens.add(token);
+ return token;
+ }
@Override
public IBinder startRemoveTransition(WindowContainerTransaction wct) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java
index 5984d48..a874a5b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java
@@ -51,4 +51,13 @@
* @return the started transition
*/
IBinder startRemoveTransition(WindowContainerTransaction wct);
+
+ /**
+ * Starts PiP transition
+ *
+ * @param wct the {@link WindowContainerTransaction} that launches the PiP
+ *
+ * @return the started transition
+ */
+ IBinder startPipTransition(WindowContainerTransaction wct);
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 6129651..6e7740d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -39,6 +39,7 @@
import android.view.WindowManager;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
@@ -312,6 +313,23 @@
return false;
}
+ /**
+ * @return a change representing a config-at-end activity for a given parent.
+ */
+ @Nullable
+ public TransitionInfo.Change getDeferConfigActivityChange(TransitionInfo info,
+ @android.annotation.NonNull WindowContainerToken parent) {
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (change.getTaskInfo() == null
+ && change.hasFlags(TransitionInfo.FLAG_CONFIG_AT_END)
+ && change.getParent() != null && change.getParent().equals(parent)) {
+ return change;
+ }
+ }
+ return null;
+ }
+
+
/** Whether a particular package is same as current pip package. */
public boolean isPackageActiveInPip(@Nullable String packageName) {
// No-op, to be handled differently in PIP1 and PIP2
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index 19428ee..e309da1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -99,7 +99,7 @@
private final List<Consumer<Boolean>> mOnIsInPipStateChangedListeners = new ArrayList<>();
// Wrapper for making Binder calls into PiP animation listener hosted in launcher's Recents.
- private PipAnimationListener mPipRecentsAnimationListener;
+ @Nullable private PipAnimationListener mPipRecentsAnimationListener;
@VisibleForTesting
interface PipAnimationListener {
@@ -378,7 +378,9 @@
tx.setLayer(overlay, Integer.MAX_VALUE);
tx.apply();
}
- mPipRecentsAnimationListener.onPipAnimationStarted();
+ if (mPipRecentsAnimationListener != null) {
+ mPipRecentsAnimationListener.onPipAnimationStarted();
+ }
}
private void setLauncherKeepClearAreaHeight(boolean visible, int height) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
index 44cc563..fc3fbe2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
@@ -222,7 +222,10 @@
pipBoundsState, mTouchState, mPipScheduler, mPipTransitionState, pipUiEventLogger,
menuController, mainExecutor,
mPipPerfHintController);
- mPipBoundsState.addOnAspectRatioChangedCallback(this::updateMinMaxSize);
+ mPipBoundsState.addOnAspectRatioChangedCallback(aspectRatio -> {
+ updateMinMaxSize(aspectRatio);
+ onAspectRatioChanged();
+ });
mMoveOnShelVisibilityChanged = () -> {
if (mIsImeShowing && mImeHeight > mShelfHeight) {
@@ -768,18 +771,19 @@
private void animateToNormalSize(Runnable callback) {
// Save the current bounds as the user-resize bounds.
mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());
-
- final Size minMenuSize = mMenuController.getEstimatedMinMenuSize();
- final Size defaultSize = mSizeSpecSource.getDefaultSize(mPipBoundsState.getAspectRatio());
- final Rect normalBounds = new Rect(0, 0, defaultSize.getWidth(), defaultSize.getHeight());
- final Rect adjustedNormalBounds = mPipBoundsAlgorithm.adjustNormalBoundsToFitMenu(
- normalBounds, minMenuSize);
-
+ final Rect adjustedNormalBounds = getAdjustedNormalBounds();
mSavedSnapFraction = mMotionHelper.animateToExpandedState(adjustedNormalBounds,
getMovementBounds(mPipBoundsState.getBounds()),
getMovementBounds(adjustedNormalBounds), callback /* callback */);
}
+ private Rect getAdjustedNormalBounds() {
+ final Size minMenuSize = mMenuController.getEstimatedMinMenuSize();
+ final Size defaultSize = mSizeSpecSource.getDefaultSize(mPipBoundsState.getAspectRatio());
+ final Rect normalBounds = new Rect(0, 0, defaultSize.getWidth(), defaultSize.getHeight());
+ return mPipBoundsAlgorithm.adjustNormalBoundsToFitMenu(normalBounds, minMenuSize);
+ }
+
private void animateToUnexpandedState(Rect restoreBounds) {
mMotionHelper.animateToUnexpandedState(restoreBounds, mSavedSnapFraction,
getMovementBounds(restoreBounds),
@@ -1065,6 +1069,10 @@
mPipBoundsAlgorithm.getMovementBounds(mPipBoundsState.getBounds(),
insetBounds, mPipBoundsState.getMovementBounds(), mIsImeShowing ? mImeHeight : 0);
mMotionHelper.onMovementBoundsChanged();
+
+ if (mPipResizeGestureHandler.getUserResizeBounds().isEmpty()) {
+ mPipResizeGestureHandler.setUserResizeBounds(getAdjustedNormalBounds());
+ }
}
private Rect getMovementBounds(Rect curBounds) {
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 b171db2..dae3c21 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
@@ -21,6 +21,7 @@
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_PIP;
@@ -30,6 +31,7 @@
import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
import static com.android.wm.shell.transition.Transitions.TRANSIT_RESIZE_PIP;
+import static com.android.wm.shell.transition.Transitions.transitTypeToString;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
@@ -44,6 +46,7 @@
import android.os.IBinder;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.WindowManager;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerToken;
@@ -52,6 +55,7 @@
import androidx.annotation.Nullable;
import com.android.internal.util.Preconditions;
+import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
@@ -193,7 +197,7 @@
@NonNull TransitionRequestInfo request) {
if (isAutoEnterInButtonNavigation(request) || isEnterPictureInPictureModeRequest(request)) {
mEnterTransition = transition;
- return getEnterPipTransaction(transition, request);
+ return getEnterPipTransaction(transition, request.getPipChange());
}
return null;
}
@@ -202,7 +206,8 @@
public void augmentRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request,
@NonNull WindowContainerTransaction outWct) {
if (isAutoEnterInButtonNavigation(request) || isEnterPictureInPictureModeRequest(request)) {
- outWct.merge(getEnterPipTransaction(transition, request), true /* transfer */);
+ outWct.merge(getEnterPipTransaction(transition, request.getPipChange()),
+ true /* transfer */);
mEnterTransition = transition;
}
}
@@ -282,6 +287,31 @@
}
@Override
+ public boolean isEnteringPip(@NonNull TransitionInfo.Change change,
+ @WindowManager.TransitionType int transitType) {
+ if (change.getTaskInfo() != null
+ && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED) {
+ // TRANSIT_TO_FRONT, though uncommon with triggering PiP, should semantically also
+ // be allowed to animate if the task in question is pinned already - see b/308054074.
+ if (transitType == TRANSIT_PIP || transitType == TRANSIT_OPEN
+ || transitType == TRANSIT_TO_FRONT) {
+ return true;
+ }
+ // This can happen if the request to enter PIP happens when we are collecting for
+ // another transition, such as TRANSIT_CHANGE (display rotation).
+ if (transitType == TRANSIT_CHANGE) {
+ return true;
+ }
+
+ // Please file a bug to handle the unexpected transition type.
+ android.util.Slog.e(TAG, "Found new PIP in transition with mis-matched type="
+ + transitTypeToString(transitType), new Throwable());
+ }
+ return false;
+ }
+
+
+ @Override
public void end() {
if (mTransitionAnimator != null && mTransitionAnimator.isRunning()) {
mTransitionAnimator.end();
@@ -662,19 +692,6 @@
}
@Nullable
- private TransitionInfo.Change getDeferConfigActivityChange(TransitionInfo info,
- @NonNull WindowContainerToken parent) {
- for (TransitionInfo.Change change : info.getChanges()) {
- if (change.getTaskInfo() == null
- && change.hasFlags(TransitionInfo.FLAG_CONFIG_AT_END)
- && change.getParent() != null && change.getParent().equals(parent)) {
- return change;
- }
- }
- return null;
- }
-
- @Nullable
private TransitionInfo.Change getChangeByToken(TransitionInfo info,
WindowContainerToken token) {
for (TransitionInfo.Change change : info.getChanges()) {
@@ -713,6 +730,10 @@
&& getFixedRotationDelta(info, pipTaskChange) == ROTATION_90) {
adjustedSourceRectHint.offset(cutoutInsets.left, cutoutInsets.top);
}
+ if (Flags.enableDesktopWindowingPip()) {
+ adjustedSourceRectHint.offset(-pipActivityChange.getStartAbsBounds().left,
+ -pipActivityChange.getStartAbsBounds().top);
+ }
} else {
// For non-valid app provided src-rect-hint, calculate one to crop into during
// app icon overlay animation.
@@ -760,9 +781,9 @@
}
private WindowContainerTransaction getEnterPipTransaction(@NonNull IBinder transition,
- @NonNull TransitionRequestInfo request) {
+ @NonNull TransitionRequestInfo.PipChange pipChange) {
// cache the original task token to check for multi-activity case later
- final ActivityManager.RunningTaskInfo pipTask = request.getPipTask();
+ final ActivityManager.RunningTaskInfo pipTask = pipChange.getTaskInfo();
PictureInPictureParams pipParams = pipTask.pictureInPictureParams;
mPipTaskListener.setPictureInPictureParams(pipParams);
mPipBoundsState.setBoundsStateForEntry(pipTask.topActivity, pipTask.topActivityInfo,
@@ -772,14 +793,18 @@
final Rect entryBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
mPipBoundsState.setBounds(entryBounds);
+ // Operate on the TF token in case we are dealing with AE case; this should avoid marking
+ // activities in other TFs as config-at-end.
+ WindowContainerToken token = pipChange.getTaskFragmentToken();
WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.movePipActivityToPinnedRootTask(pipTask.token, entryBounds);
- wct.deferConfigToTransitionEnd(pipTask.token);
+ wct.movePipActivityToPinnedRootTask(token, entryBounds);
+ wct.deferConfigToTransitionEnd(token);
return wct;
}
private boolean isAutoEnterInButtonNavigation(@NonNull TransitionRequestInfo requestInfo) {
- final ActivityManager.RunningTaskInfo pipTask = requestInfo.getPipTask();
+ final ActivityManager.RunningTaskInfo pipTask = requestInfo.getPipChange() != null
+ ? requestInfo.getPipChange().getTaskInfo() : null;
if (pipTask == null) {
return false;
}
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 b922cd0..0869caa 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
@@ -550,7 +550,9 @@
groupedTasks.add(GroupedTaskInfo.forSplitTasks(taskInfo, pairedTaskInfo,
mTaskSplitBoundsMap.get(pairedTaskId)));
} else {
- if (Flags.enableRefactorTaskThumbnail() && isWallpaperTask(taskInfo)) {
+ if (
+ Flags.enableUseTopVisibleActivityForExcludeFromRecentTask()
+ && isWallpaperTask(taskInfo)) {
// Don't add the wallpaper task as an entry in grouped tasks
continue;
}
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 37d5878..032dac9 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
@@ -1317,6 +1317,9 @@
// otherwise a new transition will notify the relevant observers
if (returningToApp && allAppsAreTranslucent(mPausingTasks)) {
mHomeTransitionObserver.notifyHomeVisibilityChanged(true);
+ } else if (!toHome && mState == STATE_NEW_TASK
+ && allAppsAreTranslucent(mOpeningTasks)) {
+ // We are opening a translucent app. Launcher is still visible so we do nothing.
} else if (!toHome) {
// For some transitions, we may have notified home activity that it became
// visible. We need to notify the observer that we are no longer going home.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index a368245..2998a07 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -512,6 +512,11 @@
mStageCoordinator.getStageBounds(outTopOrLeftBounds, outBottomOrRightBounds);
}
+ /** Get the parent-based coordinates for split stages. */
+ public void getRefStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
+ mStageCoordinator.getRefStageBounds(outTopOrLeftBounds, outBottomOrRightBounds);
+ }
+
public void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) {
mStageCoordinator.registerSplitScreenListener(listener);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index fdc40411..ba724ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -1776,6 +1776,11 @@
outBottomOrRightBounds.set(mSplitLayout.getBottomRightBounds());
}
+ void getRefStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
+ outTopOrLeftBounds.set(mSplitLayout.getTopLeftRefBounds());
+ outBottomOrRightBounds.set(mSplitLayout.getBottomRightRefBounds());
+ }
+
private void runForActiveStages(Consumer<StageTaskListener> consumer) {
mStageOrderOperator.getActiveStages().forEach(consumer);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
index 82c0aaf..361d7663 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
@@ -186,6 +186,7 @@
*/
public void setObscuredTouchRect(Rect obscuredRect) {
mObscuredTouchRegion = obscuredRect != null ? new Region(obscuredRect) : null;
+ invalidate();
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 92d1f9c..41c0a11 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -287,6 +287,7 @@
MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING, transition));
// Postpone transition splitting to later.
WindowContainerTransaction out = new WindowContainerTransaction();
+ mPipHandler.augmentRequest(transition, request, out);
return out;
} else if (request.getRemoteTransition() != null
&& TransitionUtil.isOpeningType(request.getType())
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
index 3d3de88..03ded73 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.transition;
+import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static com.android.wm.shell.transition.DefaultMixedHandler.subCopy;
@@ -37,6 +38,8 @@
import com.android.wm.shell.splitscreen.StageCoordinator;
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
+import java.util.List;
+
class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition {
private final UnfoldTransitionHandler mUnfoldHandler;
private final ActivityEmbeddingController mActivityEmbeddingController;
@@ -127,6 +130,13 @@
}
}
+ TransitionInfo.Change pipActivityChange = null;
+ if (pipChange != null) {
+ pipActivityChange = mPipHandler.getDeferConfigActivityChange(
+ info, pipChange.getContainer());
+ everythingElse.getChanges().remove(pipActivityChange);
+ }
+
final Transitions.TransitionFinishCallback finishCB = (wct) -> {
--mInFlightSubAnimations;
joinFinishArgs(wct);
@@ -139,13 +149,23 @@
return false;
}
- // PIP window should always be on the highest Z order.
- if (pipChange != null) {
+ if (pipChange != null && pipActivityChange == null) {
+ // We are operating on a single PiP task for the enter animation here.
mInFlightSubAnimations = 2;
+ // PIP window should always be on the highest Z order.
mPipHandler.startEnterAnimation(
pipChange, startTransaction.setLayer(pipChange.getLeash(), Integer.MAX_VALUE),
finishTransaction,
finishCB);
+ } else if (pipActivityChange != null) {
+ // If there is both a PiP task and a PiP config-at-end activity change,
+ // put them into a separate TransitionInfo, and send to be animated as TRANSIT_PIP.
+ mInFlightSubAnimations = 2;
+ TransitionInfo pipInfo = subCopy(info, TRANSIT_PIP, false /* withChanges */);
+ pipInfo.getChanges().addAll(List.of(pipChange, pipActivityChange));
+ mPipHandler.startAnimation(mTransition, pipInfo,
+ startTransaction.setLayer(pipChange.getLeash(), Integer.MAX_VALUE),
+ finishTransaction, finishCB);
} else {
mInFlightSubAnimations = 1;
}
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 885f3db..0b91966 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
@@ -68,6 +68,8 @@
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.FocusTransitionObserver;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
/**
@@ -90,6 +92,7 @@
private final Transitions mTransitions;
private final Region mExclusionRegion = Region.obtain();
private final InputManager mInputManager;
+ private final WindowDecorViewHostSupplier<WindowDecorViewHost> mWindowDecorViewHostSupplier;
private TaskOperations mTaskOperations;
private FocusTransitionObserver mFocusTransitionObserver;
@@ -130,7 +133,8 @@
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
SyncTransactionQueue syncQueue,
Transitions transitions,
- FocusTransitionObserver focusTransitionObserver) {
+ FocusTransitionObserver focusTransitionObserver,
+ WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier) {
mContext = context;
mMainExecutor = shellExecutor;
mMainHandler = mainHandler;
@@ -143,6 +147,7 @@
mSyncQueue = syncQueue;
mTransitions = transitions;
mFocusTransitionObserver = focusTransitionObserver;
+ mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
mTaskOperations = new TaskOperations(null, mContext, mSyncQueue);
}
@@ -332,7 +337,8 @@
mMainHandler,
mBgExecutor,
mMainChoreographer,
- mSyncQueue);
+ mSyncQueue,
+ mWindowDecorViewHostSupplier);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
final FluidResizeTaskPositioner taskPositioner =
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index aa954fb..23bb2aa 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
@@ -58,6 +58,8 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
/**
@@ -90,8 +92,10 @@
Handler handler,
@ShellBackgroundThread ShellExecutor bgExecutor,
Choreographer choreographer,
- SyncTransactionQueue syncQueue) {
- super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface);
+ SyncTransactionQueue syncQueue,
+ @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier) {
+ super(context, userContext, displayController, taskOrganizer, taskInfo,
+ taskSurface, windowDecorViewHostSupplier);
mHandler = handler;
mBgExecutor = bgExecutor;
mChoreographer = choreographer;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt
index 4d95cde..01fc644 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt
@@ -19,19 +19,18 @@
import android.app.ActivityManager.RunningTaskInfo
import android.content.Context
import android.graphics.Point
-import android.graphics.Rect
+import android.view.View
import android.view.WindowManager
import android.window.TaskSnapshot
import androidx.compose.ui.graphics.toArgb
+import com.android.wm.shell.R
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.shared.multiinstance.ManageWindowsViewContainer
-import com.android.wm.shell.shared.split.SplitScreenConstants
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer
+import com.android.wm.shell.windowdecor.common.calculateMenuPosition
import com.android.wm.shell.windowdecor.common.DecorThemeUtil
-import com.android.wm.shell.windowdecor.extension.isFullscreen
-import com.android.wm.shell.windowdecor.extension.isMultiWindow
/**
* Implementation of [ManageWindowsViewContainer] meant to be used in desktop header and app
@@ -59,35 +58,19 @@
}
private fun calculateMenuPosition(): Point {
- val position = Point()
- val nonFreeformX = (captionX + (captionWidth / 2) - (menuView.menuWidth / 2))
- when {
- callerTaskInfo.isFreeform -> {
- val taskBounds = callerTaskInfo.getConfiguration().windowConfiguration.bounds
- position.set(taskBounds.left, taskBounds.top)
- }
- callerTaskInfo.isFullscreen -> {
- position.set(nonFreeformX, 0)
- }
- callerTaskInfo.isMultiWindow -> {
- val splitPosition = splitScreenController.getSplitPosition(callerTaskInfo.taskId)
- val leftOrTopStageBounds = Rect()
- val rightOrBottomStageBounds = Rect()
- splitScreenController.getStageBounds(leftOrTopStageBounds, rightOrBottomStageBounds)
- // TODO(b/343561161): This needs to be calculated differently if the task is in
- // top/bottom split.
- when (splitPosition) {
- SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT -> {
- position.set(leftOrTopStageBounds.width() + nonFreeformX, /* y= */ 0)
- }
-
- SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT -> {
- position.set(nonFreeformX, /* y= */ 0)
- }
- }
- }
- }
- return position
+ return calculateMenuPosition(
+ splitScreenController,
+ callerTaskInfo,
+ marginStart = 0,
+ marginTop = context.resources.getDimensionPixelSize(
+ R.dimen.desktop_mode_handle_menu_margin_top
+ ),
+ captionX,
+ 0,
+ captionWidth,
+ menuView.menuWidth,
+ context.isRtl()
+ )
}
override fun addToContainer(menuView: ManageWindowsView) {
@@ -109,4 +92,7 @@
override fun removeFromContainer() {
menuViewContainer?.releaseView()
}
+
+ private fun Context.isRtl() =
+ resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL
}
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 e8b02dc..0f5813c 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
@@ -79,6 +79,7 @@
import android.view.SurfaceControl.Transaction;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.window.DesktopModeFlags;
import android.window.TaskSnapshot;
@@ -139,6 +140,8 @@
import com.android.wm.shell.transition.FocusTransitionObserver;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionRegionListener;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
import com.android.wm.shell.windowdecor.extension.InsetsStateKt;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
@@ -217,6 +220,7 @@
private boolean mInImmersiveMode;
private final String mSysUIPackageName;
private final AssistContentRequester mAssistContentRequester;
+ private final WindowDecorViewHostSupplier<WindowDecorViewHost> mWindowDecorViewHostSupplier;
private final DisplayChangeController.OnDisplayChangingListener mOnDisplayChangingListener;
private final ISystemGestureExclusionListener mGestureExclusionListener =
@@ -260,6 +264,7 @@
InteractionJankMonitor interactionJankMonitor,
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
+ @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier,
MultiInstanceHelper multiInstanceHelper,
Optional<DesktopTasksLimiter> desktopTasksLimiter,
AppHandleEducationController appHandleEducationController,
@@ -289,6 +294,7 @@
desktopImmersiveController,
genericLinksParser,
assistContentRequester,
+ windowDecorViewHostSupplier,
multiInstanceHelper,
new DesktopModeWindowDecoration.Factory(),
new InputMonitorFactory(),
@@ -329,6 +335,7 @@
DesktopImmersiveController desktopImmersiveController,
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
+ @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier,
MultiInstanceHelper multiInstanceHelper,
DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
InputMonitorFactory inputMonitorFactory,
@@ -381,6 +388,7 @@
mWindowDecorCaptionHandleRepository = windowDecorCaptionHandleRepository;
mActivityOrientationChangeHandler = activityOrientationChangeHandler;
mAssistContentRequester = assistContentRequester;
+ mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
mOnDisplayChangingListener = (displayId, fromRotation, toRotation, displayAreaInfo, t) -> {
DesktopModeWindowDecoration decoration;
RunningTaskInfo taskInfo;
@@ -580,8 +588,16 @@
private void openHandleMenu(int taskId) {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
- decoration.createHandleMenu(checkNumberOfOtherInstances(decoration.mTaskInfo)
- >= MANAGE_WINDOWS_MINIMUM_INSTANCES);
+ // TODO(b/379873022): Run the instance check and the AssistContent request in
+ // createHandleMenu on the same bg thread dispatch.
+ mBgExecutor.execute(() -> {
+ final int numOfInstances = checkNumberOfOtherInstances(decoration.mTaskInfo);
+ mMainExecutor.execute(() -> {
+ decoration.createHandleMenu(
+ numOfInstances >= MANAGE_WINDOWS_MINIMUM_INSTANCES
+ );
+ });
+ });
}
private void onToggleSizeInteraction(
@@ -755,12 +771,20 @@
return;
}
decoration.closeHandleMenu();
- decoration.createManageWindowsMenu(getTaskSnapshots(decoration.mTaskInfo),
- /* onIconClickListener= */(Integer requestedTaskId) -> {
- decoration.closeManageWindowsMenu();
- mDesktopTasksController.openInstance(decoration.mTaskInfo, requestedTaskId);
- return Unit.INSTANCE;
- });
+ mBgExecutor.execute(() -> {
+ final ArrayList<Pair<Integer, TaskSnapshot>> snapshotList =
+ getTaskSnapshots(decoration.mTaskInfo);
+ mMainExecutor.execute(() -> decoration.createManageWindowsMenu(
+ snapshotList,
+ /* onIconClickListener= */ (Integer requestedTaskId) -> {
+ decoration.closeManageWindowsMenu();
+ mDesktopTasksController.openInstance(decoration.mTaskInfo,
+ requestedTaskId);
+ return Unit.INSTANCE;
+ }
+ )
+ );
+ });
}
private ArrayList<Pair<Integer, TaskSnapshot>> getTaskSnapshots(
@@ -965,8 +989,11 @@
}
if (mInputManager != null
&& !Flags.enableAccessibleCustomHeaders()) {
- // Pilfer so that windows below receive cancellations for this gesture.
- mInputManager.pilferPointers(v.getViewRootImpl().getInputToken());
+ ViewRootImpl viewRootImpl = v.getViewRootImpl();
+ if (viewRootImpl != null) {
+ // Pilfer so that windows below receive cancellations for this gesture.
+ mInputManager.pilferPointers(viewRootImpl.getInputToken());
+ }
}
if (isUpOrCancel) {
// Gesture is finished, reset state.
@@ -1607,7 +1634,9 @@
}
final DesktopModeWindowDecoration windowDecoration =
mDesktopModeWindowDecorFactory.create(
- mContext,
+ Flags.enableBugFixesForSecondaryDisplay()
+ ? mDisplayController.getDisplayContext(taskInfo.displayId)
+ : mContext,
mContext.createContextAsUser(UserHandle.of(taskInfo.userId), 0 /* flags */),
mDisplayController,
mSplitScreenController,
@@ -1623,6 +1652,7 @@
mRootTaskDisplayAreaOrganizer,
mGenericLinksParser,
mAssistContentRequester,
+ mWindowDecorViewHostSupplier,
mMultiInstanceHelper,
mWindowDecorCaptionHandleRepository,
mDesktopModeEventLogger);
@@ -1802,11 +1832,10 @@
// TODO(b/336289597): Rather than returning number of instances, return a list of valid
// instances, then refer to the list's size and reuse the list for Manage Windows menu.
final IActivityTaskManager activityTaskManager = ActivityTaskManager.getService();
- final IActivityManager activityManager = ActivityManager.getService();
try {
return activityTaskManager.getRecentTasks(Integer.MAX_VALUE,
ActivityManager.RECENT_WITH_EXCLUDED,
- activityManager.getCurrentUserId()).getList().stream().filter(
+ info.userId).getList().stream().filter(
recentTaskInfo -> (recentTaskInfo.taskId != info.taskId
&& recentTaskInfo.baseActivity != null
&& recentTaskInfo.baseActivity.getPackageName()
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 e7985de..6562f38 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
@@ -43,7 +43,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.WindowConfiguration.WindowingMode;
import android.app.assist.AssistContent;
@@ -106,6 +105,8 @@
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
import com.android.wm.shell.shared.multiinstance.ManageWindowsViewContainer;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder;
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
@@ -128,7 +129,6 @@
*/
public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLinearLayout> {
private static final String TAG = "DesktopModeWindowDecoration";
- private static final int CAPTURED_LINK_TIMEOUT_MS = 7000;
@VisibleForTesting
static final long CLOSE_MAXIMIZE_MENU_DELAY_MS = 150L;
@@ -158,14 +158,11 @@
private Function0<Unit> mOnMaximizeHoverListener;
private DragPositioningCallback mDragPositioningCallback;
private DragResizeInputListener mDragResizeListener;
- private Runnable mCurrentViewHostRunnable = null;
private RelayoutParams mRelayoutParams = new RelayoutParams();
private DisabledEdge mDisabledResizingEdge =
NONE;
private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
new WindowDecoration.RelayoutResult<>();
- private final Runnable mViewHostRunnable =
- () -> updateViewHost(mRelayoutParams, null /* onDrawTransaction */, mResult);
private final Point mPositionInParent = new Point();
private HandleMenu mHandleMenu;
@@ -203,7 +200,6 @@
// being hovered. There's a small delay after stopping the hover, to allow a quick reentry
// to cancel the close.
private final Runnable mCloseMaximizeWindowRunnable = this::closeMaximizeMenu;
- private final Runnable mCapturedLinkExpiredRunnable = this::onCapturedLinkExpired;
private final MultiInstanceHelper mMultiInstanceHelper;
private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository;
private final DesktopUserRepositories mDesktopUserRepositories;
@@ -225,6 +221,7 @@
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
+ @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier,
MultiInstanceHelper multiInstanceHelper,
WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
DesktopModeEventLogger desktopModeEventLogger) {
@@ -236,6 +233,7 @@
WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper(
context.getSystemService(WindowManager.class)),
new SurfaceControlViewHostFactory() {},
+ windowDecorViewHostSupplier,
DefaultMaximizeMenuFactory.INSTANCE,
DefaultHandleMenuFactory.INSTANCE, multiInstanceHelper,
windowDecorCaptionHandleRepository, desktopModeEventLogger);
@@ -264,15 +262,16 @@
Supplier<SurfaceControl> surfaceControlSupplier,
WindowManagerWrapper windowManagerWrapper,
SurfaceControlViewHostFactory surfaceControlViewHostFactory,
+ @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier,
MaximizeMenuFactory maximizeMenuFactory,
HandleMenuFactory handleMenuFactory,
MultiInstanceHelper multiInstanceHelper,
WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
DesktopModeEventLogger desktopModeEventLogger) {
- super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
- surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
+ super(context, userContext, displayController, taskOrganizer, taskInfo,
+ taskSurface, surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
windowContainerTransactionSupplier, surfaceControlSupplier,
- surfaceControlViewHostFactory, desktopModeEventLogger);
+ surfaceControlViewHostFactory, windowDecorViewHostSupplier, desktopModeEventLogger);
mSplitScreenController = splitScreenController;
mHandler = handler;
mBgExecutor = bgExecutor;
@@ -451,78 +450,7 @@
boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
boolean hasGlobalFocus, @NonNull Region displayExclusionRegion) {
Trace.beginSection("DesktopModeWindowDecoration#relayout");
- if (taskInfo.isFreeform()) {
- // The Task is in Freeform mode -> show its header in sync since it's an integral part
- // of the window itself - a delayed header might cause bad UX.
- relayoutInSync(taskInfo, startT, finishT, applyStartTransactionOnDraw,
- shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus, displayExclusionRegion);
- } else {
- // The Task is outside Freeform mode -> allow the handle view to be delayed since the
- // handle is just a small addition to the window.
- relayoutWithDelayedViewHost(taskInfo, startT, finishT, applyStartTransactionOnDraw,
- shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus, displayExclusionRegion);
- }
- Trace.endSection();
- }
- /** Run the whole relayout phase immediately without delay. */
- private void relayoutInSync(ActivityManager.RunningTaskInfo taskInfo,
- SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
- boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
- boolean hasGlobalFocus, @NonNull Region displayExclusionRegion) {
- // Clear the current ViewHost runnable as we will update the ViewHost here
- clearCurrentViewHostRunnable();
- updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT, applyStartTransactionOnDraw,
- shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus, displayExclusionRegion);
- if (mResult.mRootView != null) {
- updateViewHost(mRelayoutParams, startT, mResult);
- }
- }
-
- /**
- * Clear the current ViewHost runnable - to ensure it doesn't run once relayout params have been
- * updated.
- */
- private void clearCurrentViewHostRunnable() {
- if (mCurrentViewHostRunnable != null) {
- mHandler.removeCallbacks(mCurrentViewHostRunnable);
- mCurrentViewHostRunnable = null;
- }
- }
-
- /**
- * Relayout the window decoration but repost some of the work, to unblock the current callstack.
- */
- private void relayoutWithDelayedViewHost(ActivityManager.RunningTaskInfo taskInfo,
- SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
- boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
- boolean hasGlobalFocus,
- @NonNull Region displayExclusionRegion) {
- if (applyStartTransactionOnDraw) {
- throw new IllegalArgumentException(
- "We cannot both sync viewhost ondraw and delay viewhost creation.");
- }
- // Clear the current ViewHost runnable as we will update the ViewHost here
- clearCurrentViewHostRunnable();
- updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT,
- false /* applyStartTransactionOnDraw */, shouldSetTaskVisibilityPositionAndCrop,
- hasGlobalFocus, displayExclusionRegion);
- if (mResult.mRootView == null) {
- // This means something blocks the window decor from showing, e.g. the task is hidden.
- // Nothing is set up in this case including the decoration surface.
- return;
- }
- // Store the current runnable so it can be removed if we start a new relayout.
- mCurrentViewHostRunnable = mViewHostRunnable;
- mHandler.post(mCurrentViewHostRunnable);
- }
-
- @SuppressLint("MissingPermission")
- private void updateRelayoutParamsAndSurfaces(ActivityManager.RunningTaskInfo taskInfo,
- SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
- boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
- boolean hasGlobalFocus, @NonNull Region displayExclusionRegion) {
- Trace.beginSection("DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces");
if (Flags.enableDesktopWindowingAppToWeb()) {
setCapturedLink(taskInfo.capturedLink, taskInfo.capturedLinkTimestamp);
}
@@ -553,9 +481,7 @@
final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
final WindowContainerTransaction wct = new WindowContainerTransaction();
- Trace.beginSection("DesktopModeWindowDecoration#relayout-updateViewsAndSurfaces");
- updateViewsAndSurfaces(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
- Trace.endSection();
+ relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
// After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
Trace.beginSection("DesktopModeWindowDecoration#relayout-applyWCT");
@@ -570,7 +496,7 @@
}
mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
disposeStatusBarInputLayer();
- Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
+ Trace.endSection(); // DesktopModeWindowDecoration#relayout
return;
}
@@ -578,7 +504,6 @@
disposeStatusBarInputLayer();
mWindowDecorViewHolder = createViewHolder();
}
- Trace.beginSection("DesktopModeWindowDecoration#relayout-binding");
final Point position = new Point();
if (isAppHandle(mWindowDecorViewHolder)) {
@@ -588,6 +513,7 @@
notifyCaptionStateChanged();
}
+ Trace.beginSection("DesktopModeWindowDecoration#relayout-bindData");
if (isAppHandle(mWindowDecorViewHolder)) {
mWindowDecorViewHolder.bindData(new AppHandleViewHolder.HandleData(
mTaskInfo, position, mResult.mCaptionWidth, mResult.mCaptionHeight,
@@ -612,7 +538,7 @@
}
updateDragResizeListener(oldDecorationSurface, inFullImmersive);
updateMaximizeMenu(startT, inFullImmersive);
- Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
+ Trace.endSection(); // DesktopModeWindowDecoration#relayout
}
private boolean isCaptionVisible() {
@@ -625,22 +551,12 @@
return;
}
mCapturedLink = new CapturedLink(capturedLink, timeStamp);
- mHandler.postDelayed(mCapturedLinkExpiredRunnable, CAPTURED_LINK_TIMEOUT_MS);
- }
-
- private void onCapturedLinkExpired() {
- mHandler.removeCallbacks(mCapturedLinkExpiredRunnable);
- if (mCapturedLink != null) {
- mCapturedLink.setExpired();
- }
}
@Nullable
private Intent getBrowserLink() {
final Uri browserLink;
- // If the captured link is available and has not expired, return the captured link.
- // Otherwise, return the generic link which is set to null if a generic link is unavailable.
- if (mCapturedLink != null && !mCapturedLink.mExpired) {
+ if (isCapturedLinkAvailable()) {
browserLink = mCapturedLink.mUri;
} else if (mWebUri != null) {
browserLink = mWebUri;
@@ -752,7 +668,13 @@
}
private boolean isCapturedLinkAvailable() {
- return mCapturedLink != null && !mCapturedLink.mExpired;
+ return mCapturedLink != null && !mCapturedLink.mUsed;
+ }
+
+ private void onCapturedLinkUsed() {
+ if (mCapturedLink != null) {
+ mCapturedLink.setUsed();
+ }
}
private void notifyNoCaptionHandle() {
@@ -897,6 +819,10 @@
relayoutParams.mCaptionWidthId = getCaptionWidthId(relayoutParams.mLayoutResId);
relayoutParams.mHasGlobalFocus = hasGlobalFocus;
relayoutParams.mDisplayExclusionRegion.set(displayExclusionRegion);
+ // Allow the handle view to be delayed since the handle is just a small addition to the
+ // window, whereas the header cannot be delayed because it is expected to be visible from
+ // the first frame.
+ relayoutParams.mAsyncViewHost = isAppHandle;
final boolean showCaption;
if (Flags.enableFullyImmersiveInDesktop()) {
@@ -1438,7 +1364,7 @@
/* onAspectRatioSettingsClickListener= */ mOnChangeAspectRatioClickListener,
/* openInBrowserClickListener= */ (intent) -> {
mOpenInBrowserClickListener.accept(intent);
- onCapturedLinkExpired();
+ onCapturedLinkUsed();
if (Flags.enableDesktopWindowingAppToWebEducationIntegration()) {
mWindowDecorCaptionHandleRepository.onAppToWebUsage();
}
@@ -1703,7 +1629,6 @@
mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
disposeResizeVeil();
disposeStatusBarInputLayer();
- clearCurrentViewHostRunnable();
if (canEnterDesktopMode(mContext) && isEducationEnabled()) {
notifyNoCaptionHandle();
}
@@ -1840,6 +1765,8 @@
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
+ @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost>
+ windowDecorViewHostSupplier,
MultiInstanceHelper multiInstanceHelper,
WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
DesktopModeEventLogger desktopModeEventLogger) {
@@ -1860,6 +1787,7 @@
rootTaskDisplayAreaOrganizer,
genericLinksParser,
assistContentRequester,
+ windowDecorViewHostSupplier,
multiInstanceHelper,
windowDecorCaptionHandleRepository,
desktopModeEventLogger);
@@ -1870,16 +1798,15 @@
static class CapturedLink {
private final long mTimeStamp;
private final Uri mUri;
- private boolean mExpired;
+ private boolean mUsed;
CapturedLink(@NonNull Uri uri, long timeStamp) {
mUri = uri;
mTimeStamp = timeStamp;
- mExpired = false;
}
- void setExpired() {
- mExpired = true;
+ private void setUsed() {
+ mUsed = true;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index 049b8d6..1179b0c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -47,12 +47,12 @@
import androidx.core.view.isGone
import com.android.window.flags.Flags
import com.android.wm.shell.R
-import com.android.wm.shell.apptoweb.isBrowserApp
import com.android.wm.shell.shared.split.SplitScreenConstants
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer
import com.android.wm.shell.windowdecor.common.DecorThemeUtil
+import com.android.wm.shell.windowdecor.common.calculateMenuPosition
import com.android.wm.shell.windowdecor.extension.isFullscreen
import com.android.wm.shell.windowdecor.extension.isMultiWindow
import com.android.wm.shell.windowdecor.extension.isPinned
@@ -240,7 +240,19 @@
val menuX: Int
val menuY: Int
val taskBounds = taskInfo.getConfiguration().windowConfiguration.bounds
- updateGlobalMenuPosition(taskBounds, captionX, captionY)
+ globalMenuPosition.set(
+ calculateMenuPosition(
+ splitScreenController,
+ taskInfo,
+ marginStart = marginMenuStart,
+ marginMenuTop,
+ captionX,
+ captionY,
+ captionWidth,
+ menuWidth,
+ context.isRtl()
+ )
+ )
if (layoutResId == R.layout.desktop_mode_app_header) {
// Align the handle menu to the start of the header.
menuX = if (context.isRtl()) {
@@ -265,53 +277,6 @@
handleMenuPosition.set(menuX.toFloat(), menuY.toFloat())
}
- private fun updateGlobalMenuPosition(taskBounds: Rect, captionX: Int, captionY: Int) {
- val nonFreeformX = captionX + (captionWidth / 2) - (menuWidth / 2)
- when {
- taskInfo.isFreeform -> {
- if (context.isRtl()) {
- globalMenuPosition.set(
- /* x= */ taskBounds.right - menuWidth - marginMenuStart,
- /* y= */ taskBounds.top + captionY + marginMenuTop
- )
- } else {
- globalMenuPosition.set(
- /* x= */ taskBounds.left + marginMenuStart,
- /* y= */ taskBounds.top + captionY + marginMenuTop
- )
- }
- }
- taskInfo.isFullscreen -> {
- globalMenuPosition.set(
- /* x = */ nonFreeformX,
- /* y = */ marginMenuTop + captionY
- )
- }
- taskInfo.isMultiWindow -> {
- val splitPosition = splitScreenController.getSplitPosition(taskInfo.taskId)
- val leftOrTopStageBounds = Rect()
- val rightOrBottomStageBounds = Rect()
- splitScreenController.getStageBounds(leftOrTopStageBounds, rightOrBottomStageBounds)
- // TODO(b/343561161): This needs to be calculated differently if the task is in
- // top/bottom split.
- when (splitPosition) {
- SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT -> {
- globalMenuPosition.set(
- /* x = */ leftOrTopStageBounds.width() + nonFreeformX,
- /* y = */ captionY + marginMenuTop
- )
- }
- SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT -> {
- globalMenuPosition.set(
- /* x = */ nonFreeformX,
- /* y = */ captionY + marginMenuTop
- )
- }
- }
- }
- }
- }
-
/**
* Update pill layout, in case task changes have caused positioning to change.
*/
@@ -374,8 +339,6 @@
)
if (splitScreenController.getSplitPosition(taskInfo.taskId)
== SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT) {
- // TODO(b/343561161): This also needs to be calculated differently if
- // the task is in top/bottom split.
val leftStageBounds = Rect()
splitScreenController.getStageBounds(leftStageBounds, Rect())
inputRelativeToMenu.x += leftStageBounds.width().toFloat()
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 584ee39..5d1bedb 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
@@ -61,6 +61,8 @@
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams.OccludingCaptionElement;
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
import com.android.wm.shell.windowdecor.extension.InsetsStateKt;
import java.util.ArrayList;
@@ -88,10 +90,10 @@
implements AutoCloseable {
/**
- * The Z-order of {@link #mCaptionContainerSurface}.
+ * The Z-order of the caption surface.
* <p>
* We use {@link #mDecorationContainerSurface} to define input window for task resizing; by
- * layering it in front of {@link #mCaptionContainerSurface}, we can allow it to handle input
+ * layering it in front of the caption surface, we can allow it to handle input
* prior to caption view itself, treating corner inputs as resize events rather than
* repositioning.
*/
@@ -100,7 +102,7 @@
* The Z-order of the task input sink in {@link DragPositioningCallback}.
* <p>
* This task input sink is used to prevent undesired dispatching of motion events out of task
- * bounds; by layering it behind {@link #mCaptionContainerSurface}, we allow captions to handle
+ * bounds; by layering it behind the caption surface, we allow captions to handle
* input events first.
*/
static final int INPUT_SINK_Z_ORDER = -2;
@@ -126,6 +128,8 @@
final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;
final Supplier<WindowContainerTransaction> mWindowContainerTransactionSupplier;
final SurfaceControlViewHostFactory mSurfaceControlViewHostFactory;
+ @NonNull private final WindowDecorViewHostSupplier<WindowDecorViewHost>
+ mWindowDecorViewHostSupplier;
private final DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener =
new DisplayController.OnDisplaysChangedListener() {
@Override
@@ -150,9 +154,7 @@
Display mDisplay;
SurfaceControl mDecorationContainerSurface;
- SurfaceControl mCaptionContainerSurface;
- private CaptionWindowlessWindowManager mCaptionWindowManager;
- private SurfaceControlViewHost mViewHost;
+ private WindowDecorViewHost mViewHost;
private Configuration mWindowDecorConfig;
TaskDragResizer mTaskDragResizer;
boolean mIsCaptionVisible;
@@ -173,11 +175,13 @@
DisplayController displayController,
ShellTaskOrganizer taskOrganizer,
RunningTaskInfo taskInfo,
- SurfaceControl taskSurface) {
- this(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
- SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
+ SurfaceControl taskSurface,
+ @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier) {
+ this(context, userContext, displayController, taskOrganizer, taskInfo,
+ taskSurface, SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
WindowContainerTransaction::new, SurfaceControl::new,
- new SurfaceControlViewHostFactory() {}, new DesktopModeEventLogger());
+ new SurfaceControlViewHostFactory() {}, windowDecorViewHostSupplier,
+ new DesktopModeEventLogger());
}
WindowDecoration(
@@ -192,6 +196,7 @@
Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
Supplier<SurfaceControl> surfaceControlSupplier,
SurfaceControlViewHostFactory surfaceControlViewHostFactory,
+ @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier,
@NonNull DesktopModeEventLogger desktopModeEventLogger
) {
mContext = context;
@@ -205,6 +210,7 @@
mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
mWindowContainerTransactionSupplier = windowContainerTransactionSupplier;
mSurfaceControlViewHostFactory = surfaceControlViewHostFactory;
+ mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
final InsetsState insetsState = mDisplayController.getInsetsState(mTaskInfo.displayId);
mIsStatusBarVisible = insetsState != null
@@ -240,15 +246,7 @@
void relayout(RelayoutParams params, SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT, WindowContainerTransaction wct, T rootView,
RelayoutResult<T> outResult) {
- updateViewsAndSurfaces(params, startT, finishT, wct, rootView, outResult);
- if (outResult.mRootView != null) {
- updateViewHost(params, startT, outResult);
- }
- }
-
- protected void updateViewsAndSurfaces(RelayoutParams params,
- SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
- WindowContainerTransaction wct, T rootView, RelayoutResult<T> outResult) {
+ Trace.beginSection("WindowDecoration#relayout");
outResult.reset();
if (params.mRunningTaskInfo != null) {
mTaskInfo = params.mRunningTaskInfo;
@@ -263,17 +261,21 @@
if (params.mSetTaskVisibilityPositionAndCrop) {
finishT.hide(mTaskSurface);
}
+ Trace.endSection(); // WindowDecoration#relayout
return;
}
-
+ Trace.beginSection("WindowDecoration#relayout-inflateIfNeeded");
inflateIfNeeded(params, wct, rootView, oldLayoutResId, outResult);
- if (outResult.mRootView == null) {
- // Didn't manage to create a root view, early out.
+ Trace.endSection();
+ final boolean hasCaptionView = outResult.mRootView != null;
+ if (!hasCaptionView) {
+ Trace.endSection(); // WindowDecoration#relayout
return;
}
- rootView = null; // Clear it just in case we use it accidentally
+ Trace.beginSection("WindowDecoration#relayout-updateCaptionVisibility");
updateCaptionVisibility(outResult.mRootView, params);
+ Trace.endSection();
final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds();
outResult.mWidth = taskBounds.width();
@@ -288,10 +290,65 @@
outResult.mCaptionY = 0;
outResult.mCaptionTopPadding = params.mCaptionTopPadding;
+ Trace.beginSection("relayout-createViewHostIfNeeded");
+ createViewHostIfNeeded(mDecorWindowContext, mDisplay);
+ Trace.endSection();
+
+ Trace.beginSection("WindowDecoration#relayout-updateSurfacesAndInsets");
+ final SurfaceControl captionSurface = mViewHost.getSurfaceControl();
updateDecorationContainerSurface(startT, outResult);
- updateCaptionContainerSurface(startT, outResult);
+ updateCaptionContainerSurface(captionSurface, startT, outResult);
updateCaptionInsets(params, wct, outResult, taskBounds);
updateTaskSurface(params, startT, finishT, outResult);
+ Trace.endSection();
+
+ Trace.beginSection("WindowDecoration#relayout-updateViewHost");
+ outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0);
+ final Rect localCaptionBounds = new Rect(
+ outResult.mCaptionX,
+ outResult.mCaptionY,
+ outResult.mCaptionX + outResult.mCaptionWidth,
+ outResult.mCaptionY + outResult.mCaptionHeight);
+ final Region touchableRegion = params.mLimitTouchRegionToSystemAreas
+ ? calculateLimitedTouchableRegion(params, localCaptionBounds)
+ : null;
+ updateViewHierarchy(params, outResult, startT, touchableRegion);
+ Trace.endSection();
+
+ Trace.endSection(); // WindowDecoration#relayout
+ }
+
+ private void createViewHostIfNeeded(@NonNull Context context, @NonNull Display display) {
+ if (mViewHost == null) {
+ mViewHost = mWindowDecorViewHostSupplier.acquire(context, display);
+ }
+ }
+
+ private void updateViewHierarchy(@NonNull RelayoutParams params,
+ @NonNull RelayoutResult<T> outResult, @NonNull SurfaceControl.Transaction startT,
+ @Nullable Region touchableRegion) {
+ Trace.beginSection("WindowDecoration#updateViewHierarchy");
+ final WindowManager.LayoutParams lp =
+ new WindowManager.LayoutParams(
+ outResult.mCaptionWidth,
+ outResult.mCaptionHeight,
+ TYPE_APPLICATION,
+ FLAG_NOT_FOCUSABLE | FLAG_SPLIT_TOUCH,
+ PixelFormat.TRANSPARENT);
+ lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
+ lp.setTrustedOverlay();
+ lp.inputFeatures = params.mInputFeatures;
+ if (params.mAsyncViewHost) {
+ if (params.mApplyStartTransactionOnDraw) {
+ throw new IllegalArgumentException("Cannot use sync draw tx with async relayout");
+ }
+ mViewHost.updateViewAsync(outResult.mRootView, lp, mTaskInfo.configuration,
+ touchableRegion);
+ } else {
+ mViewHost.updateView(outResult.mRootView, lp, mTaskInfo.configuration,
+ touchableRegion, params.mApplyStartTransactionOnDraw ? startT : null);
+ }
+ Trace.endSection();
}
private void inflateIfNeeded(RelayoutParams params, WindowContainerTransaction wct,
@@ -359,23 +416,13 @@
.show(mDecorationContainerSurface);
}
- private void updateCaptionContainerSurface(
+ private void updateCaptionContainerSurface(@NonNull SurfaceControl captionSurface,
SurfaceControl.Transaction startT, RelayoutResult<T> outResult) {
- if (mCaptionContainerSurface == null) {
- final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
- mCaptionContainerSurface = builder
- .setName("Caption container of Task=" + mTaskInfo.taskId)
- .setContainerLayer()
- .setParent(mDecorationContainerSurface)
- .setCallsite("WindowDecoration.updateCaptionContainerSurface")
- .build();
- }
-
- startT.setWindowCrop(mCaptionContainerSurface, outResult.mCaptionWidth,
- outResult.mCaptionHeight)
- .setPosition(mCaptionContainerSurface, outResult.mCaptionX, 0 /* y */)
- .setLayer(mCaptionContainerSurface, CAPTION_LAYER_Z_ORDER)
- .show(mCaptionContainerSurface);
+ startT.reparent(captionSurface, mDecorationContainerSurface)
+ .setWindowCrop(captionSurface, outResult.mCaptionWidth, outResult.mCaptionHeight)
+ .setPosition(captionSurface, outResult.mCaptionX, 0 /* y */)
+ .setLayer(captionSurface, CAPTION_LAYER_Z_ORDER)
+ .show(captionSurface);
}
private void updateCaptionInsets(RelayoutParams params, WindowContainerTransaction wct,
@@ -469,80 +516,6 @@
}
}
- /**
- * Updates a {@link SurfaceControlViewHost} to connect the window decoration surfaces with our
- * View hierarchy.
- *
- * @param params parameters to use from the last relayout
- * @param onDrawTransaction a transaction to apply in sync with #onDraw
- * @param outResult results to use from the last relayout
- *
- */
- protected void updateViewHost(RelayoutParams params,
- SurfaceControl.Transaction onDrawTransaction, RelayoutResult<T> outResult) {
- Trace.beginSection("CaptionViewHostLayout");
- if (mCaptionWindowManager == null) {
- // Put caption under a container surface because ViewRootImpl sets the destination frame
- // of windowless window layers and BLASTBufferQueue#update() doesn't support offset.
- mCaptionWindowManager = new CaptionWindowlessWindowManager(
- mTaskInfo.getConfiguration(), mCaptionContainerSurface);
- }
- mCaptionWindowManager.setConfiguration(mTaskInfo.getConfiguration());
- final WindowManager.LayoutParams lp =
- new WindowManager.LayoutParams(
- outResult.mCaptionWidth,
- outResult.mCaptionHeight,
- TYPE_APPLICATION,
- FLAG_NOT_FOCUSABLE | FLAG_SPLIT_TOUCH,
- PixelFormat.TRANSPARENT);
- lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
- lp.setTrustedOverlay();
- lp.inputFeatures = params.mInputFeatures;
- final Rect localCaptionBounds = new Rect(
- outResult.mCaptionX,
- outResult.mCaptionY,
- outResult.mCaptionX + outResult.mCaptionWidth,
- outResult.mCaptionY + outResult.mCaptionHeight);
- final Region touchableRegion = params.mLimitTouchRegionToSystemAreas
- ? calculateLimitedTouchableRegion(params, localCaptionBounds)
- : null;
- if (mViewHost == null) {
- Trace.beginSection("CaptionViewHostLayout-new");
- mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay,
- mCaptionWindowManager);
- if (params.mApplyStartTransactionOnDraw) {
- if (onDrawTransaction == null) {
- throw new IllegalArgumentException("Trying to sync a null Transaction");
- }
- mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction);
- }
- outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0);
- if (params.mLimitTouchRegionToSystemAreas) {
- mCaptionWindowManager.setTouchRegion(mViewHost, touchableRegion);
- }
- mViewHost.setView(outResult.mRootView, lp);
- Trace.endSection();
- } else {
- Trace.beginSection("CaptionViewHostLayout-relayout");
- if (params.mApplyStartTransactionOnDraw) {
- if (onDrawTransaction == null) {
- throw new IllegalArgumentException("Trying to sync a null Transaction");
- }
- mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction);
- }
- outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0);
- if (params.mLimitTouchRegionToSystemAreas) {
- mCaptionWindowManager.setTouchRegion(mViewHost, touchableRegion);
- }
- mViewHost.relayout(lp);
- Trace.endSection();
- }
- if (touchableRegion != null) {
- touchableRegion.recycle();
- }
- Trace.endSection(); // CaptionViewHostLayout
- }
-
@NonNull
private Region calculateLimitedTouchableRegion(
RelayoutParams params,
@@ -691,18 +664,11 @@
}
void releaseViews(WindowContainerTransaction wct) {
- if (mViewHost != null) {
- mViewHost.release();
- mViewHost = null;
- }
-
- mCaptionWindowManager = null;
-
final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
boolean released = false;
- if (mCaptionContainerSurface != null) {
- t.remove(mCaptionContainerSurface);
- mCaptionContainerSurface = null;
+ if (mViewHost != null) {
+ mWindowDecorViewHostSupplier.release(mViewHost, t);
+ mViewHost = null;
released = true;
}
@@ -855,6 +821,7 @@
boolean mIsCaptionVisible;
Configuration mWindowDecorConfig;
+ boolean mAsyncViewHost;
boolean mApplyStartTransactionOnDraw;
boolean mSetTaskVisibilityPositionAndCrop;
@@ -880,6 +847,7 @@
mApplyStartTransactionOnDraw = false;
mSetTaskVisibilityPositionAndCrop = false;
mWindowDecorConfig = null;
+ mAsyncViewHost = false;
mHasGlobalFocus = false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/DesktopMenuPositionUtility.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/DesktopMenuPositionUtility.kt
new file mode 100644
index 0000000..6def3da
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/DesktopMenuPositionUtility.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor.common
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.graphics.Point
+import android.graphics.Rect
+import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
+import com.android.wm.shell.splitscreen.SplitScreenController
+import com.android.wm.shell.windowdecor.extension.isFullscreen
+
+/** Utility function used for calculating position of desktop mode menus. */
+fun calculateMenuPosition(
+ splitScreenController: SplitScreenController,
+ taskInfo: RunningTaskInfo,
+ marginStart: Int,
+ marginTop: Int,
+ captionX: Int,
+ captionY: Int,
+ captionWidth: Int,
+ menuWidth: Int,
+ isRtl: Boolean,
+): Point {
+ if (taskInfo.isFreeform) {
+ val taskBounds = taskInfo.configuration.windowConfiguration.bounds
+ return if (isRtl) {
+ Point(
+ /* x= */ taskBounds.right - menuWidth - marginStart,
+ /* y= */ taskBounds.top + marginTop,
+ )
+ } else {
+ Point(/* x= */ taskBounds.left + marginStart, /* y= */ taskBounds.top + marginTop)
+ }
+ }
+ val nonFreeformPosition = Point(captionX + (captionWidth / 2) - (menuWidth / 2), captionY)
+ if (taskInfo.isFullscreen) {
+ return Point(nonFreeformPosition.x, nonFreeformPosition.y + marginTop)
+ }
+ // Only the splitscreen case left.
+ val splitPosition = splitScreenController.getSplitPosition(taskInfo.taskId)
+ val leftOrTopStageBounds = Rect()
+ val rightOrBottomStageBounds = Rect()
+ splitScreenController.getRefStageBounds(leftOrTopStageBounds, rightOrBottomStageBounds)
+ if (splitScreenController.isLeftRightSplit) {
+ val rightStageModifier =
+ if (splitPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+ rightOrBottomStageBounds.left
+ } else {
+ 0
+ }
+ return Point(
+ /* x = */ rightStageModifier + nonFreeformPosition.x,
+ /* y = */ nonFreeformPosition.y + marginTop,
+ )
+ } else {
+ val bottomSplitModifier =
+ if (splitPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+ rightOrBottomStageBounds.top
+ } else {
+ 0
+ }
+ return Point(
+ /* x = */ nonFreeformPosition.x,
+ /* y = */ nonFreeformPosition.y + bottomSplitModifier + marginTop,
+ )
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHost.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHost.kt
index c470eef..a205ac6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHost.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHost.kt
@@ -17,12 +17,11 @@
import android.content.Context
import android.content.res.Configuration
+import android.graphics.Region
import android.view.Display
import android.view.SurfaceControl
-import android.view.SurfaceControlViewHost
import android.view.View
import android.view.WindowManager
-import android.view.WindowlessWindowManager
import androidx.tracing.Trace
import com.android.internal.annotations.VisibleForTesting
import com.android.wm.shell.shared.annotations.ShellMainThread
@@ -30,51 +29,34 @@
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
-typealias SurfaceControlViewHostFactory =
- (Context, Display, WindowlessWindowManager, String) -> SurfaceControlViewHost
-
/**
- * A default implementation of [WindowDecorViewHost] backed by a [SurfaceControlViewHost].
+ * A default implementation of [WindowDecorViewHost] backed by a [SurfaceControlViewHostAdapter].
*
- * It does not support swapping the root view added to the VRI of the [SurfaceControlViewHost], and
- * any attempts to do will throw, which means that once a [View] is added using [updateView] or
- * [updateViewAsync], only its properties and binding may be changed, its children views may be
- * added, removed or changed and its [WindowManager.LayoutParams] may be changed. It also supports
- * asynchronously updating the view hierarchy using [updateViewAsync], in which case the update work
- * will be posted on the [ShellMainThread] with no delay.
+ * It supports asynchronously updating the view hierarchy using [updateViewAsync], in which
+ * case the update work will be posted on the [ShellMainThread] with no delay.
*/
class DefaultWindowDecorViewHost(
- private val context: Context,
+ context: Context,
@ShellMainThread private val mainScope: CoroutineScope,
- private val display: Display,
- private val surfaceControlViewHostFactory: SurfaceControlViewHostFactory = { c, d, wwm, s ->
- SurfaceControlViewHost(c, d, wwm, s)
- },
+ display: Display,
+ @VisibleForTesting val viewHostAdapter: SurfaceControlViewHostAdapter =
+ SurfaceControlViewHostAdapter(context, display),
) : WindowDecorViewHost {
-
- private val rootSurface: SurfaceControl =
- SurfaceControl.Builder()
- .setName("DefaultWindowDecorViewHost surface")
- .setContainerLayer()
- .setCallsite("DefaultWindowDecorViewHost#init")
- .build()
-
- private var wwm: WindowlessWindowManager? = null
- @VisibleForTesting var viewHost: SurfaceControlViewHost? = null
private var currentUpdateJob: Job? = null
override val surfaceControl: SurfaceControl
- get() = rootSurface
+ get() = viewHostAdapter.rootSurface
override fun updateView(
view: View,
attrs: WindowManager.LayoutParams,
configuration: Configuration,
+ touchableRegion: Region?,
onDrawTransaction: SurfaceControl.Transaction?,
) {
Trace.beginSection("DefaultWindowDecorViewHost#updateView")
clearCurrentUpdateJob()
- updateViewHost(view, attrs, configuration, onDrawTransaction)
+ updateViewHost(view, attrs, configuration, touchableRegion, onDrawTransaction)
Trace.endSection()
}
@@ -82,53 +64,41 @@
view: View,
attrs: WindowManager.LayoutParams,
configuration: Configuration,
+ touchableRegion: Region?,
) {
Trace.beginSection("DefaultWindowDecorViewHost#updateViewAsync")
clearCurrentUpdateJob()
currentUpdateJob =
mainScope.launch {
- updateViewHost(view, attrs, configuration, onDrawTransaction = null)
+ updateViewHost(
+ view,
+ attrs,
+ configuration,
+ touchableRegion,
+ onDrawTransaction = null
+ )
}
Trace.endSection()
}
override fun release(t: SurfaceControl.Transaction) {
clearCurrentUpdateJob()
- viewHost?.release()
- t.remove(rootSurface)
+ viewHostAdapter.release(t)
}
private fun updateViewHost(
view: View,
attrs: WindowManager.LayoutParams,
configuration: Configuration,
+ touchableRegion: Region?,
onDrawTransaction: SurfaceControl.Transaction?,
) {
Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost")
- if (wwm == null) {
- wwm = WindowlessWindowManager(configuration, rootSurface, null)
+ viewHostAdapter.prepareViewHost(configuration, touchableRegion)
+ onDrawTransaction?.let {
+ viewHostAdapter.applyTransactionOnDraw(it)
}
- requireWindowlessWindowManager().setConfiguration(configuration)
- if (viewHost == null) {
- viewHost =
- surfaceControlViewHostFactory.invoke(
- context,
- display,
- requireWindowlessWindowManager(),
- "DefaultWindowDecorViewHost#updateViewHost",
- )
- }
- onDrawTransaction?.let { requireViewHost().rootSurfaceControl.applyTransactionOnDraw(it) }
- if (requireViewHost().view == null) {
- Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost-setView")
- requireViewHost().setView(view, attrs)
- Trace.endSection()
- } else {
- check(requireViewHost().view == view) { "Changing view is not allowed" }
- Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost-relayout")
- requireViewHost().relayout(attrs)
- Trace.endSection()
- }
+ viewHostAdapter.updateView(view, attrs)
Trace.endSection()
}
@@ -136,12 +106,4 @@
currentUpdateJob?.cancel()
currentUpdateJob = null
}
-
- private fun requireWindowlessWindowManager(): WindowlessWindowManager {
- return wwm ?: error("Expected non-null windowless window manager")
- }
-
- private fun requireViewHost(): SurfaceControlViewHost {
- return viewHost ?: error("Expected non-null view host")
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostSupplier.kt
index 27ffd6c..7821619 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostSupplier.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostSupplier.kt
@@ -24,14 +24,15 @@
/**
* A supplier of [DefaultWindowDecorViewHost]s. It creates a new one every time one is requested.
*/
-class DefaultWindowDecorViewHostSupplier(@ShellMainThread private val mainScope: CoroutineScope) :
- WindowDecorViewHostSupplier<DefaultWindowDecorViewHost> {
+class DefaultWindowDecorViewHostSupplier(
+ @ShellMainThread private val mainScope: CoroutineScope
+) : WindowDecorViewHostSupplier<WindowDecorViewHost> {
- override fun acquire(context: Context, display: Display): DefaultWindowDecorViewHost {
+ override fun acquire(context: Context, display: Display): WindowDecorViewHost {
return DefaultWindowDecorViewHost(context, mainScope, display)
}
- override fun release(viewHost: DefaultWindowDecorViewHost, t: SurfaceControl.Transaction) {
+ override fun release(viewHost: WindowDecorViewHost, t: SurfaceControl.Transaction) {
viewHost.release(t)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/PooledWindowDecorViewHostSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/PooledWindowDecorViewHostSupplier.kt
new file mode 100644
index 0000000..47cfaee
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/PooledWindowDecorViewHostSupplier.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.common.viewhost
+
+import android.content.Context
+import android.os.Trace
+import android.util.Pools
+import android.view.Display
+import android.view.SurfaceControl
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.sysui.ShellInit
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * A [WindowDecorViewHostSupplier] backed by a pool to allow recycling view hosts which may be
+ * expensive to recreate for each new or updated window decoration.
+ *
+ * Callers can obtain a [WindowDecorViewHost] using [acquire], which will return a pooled object if
+ * available, or create a new instance and return it if needed. When finished using a
+ * [WindowDecorViewHost], it must be released using [release] to allow it to be sent back into the
+ * pool and reused later on.
+ *
+ * This class also supports pre-warming [ReusableWindowDecorViewHost] instances, which will be put
+ * into the pool immediately after creation.
+ */
+class PooledWindowDecorViewHostSupplier(
+ private val context: Context,
+ @ShellMainThread private val mainScope: CoroutineScope,
+ shellInit: ShellInit,
+ maxPoolSize: Int,
+ private val preWarmSize: Int,
+) : WindowDecorViewHostSupplier<WindowDecorViewHost> {
+
+ private val pool: Pools.Pool<WindowDecorViewHost> = Pools.SynchronizedPool(maxPoolSize)
+ private var nextDecorViewHostId = 0
+
+ init {
+ require(preWarmSize <= maxPoolSize) { "Pre-warm size should not exceed pool size" }
+ shellInit.addInitCallback(this::onShellInit, this)
+ }
+
+ private fun onShellInit() {
+ if (preWarmSize <= 0) {
+ return
+ }
+ preWarmViewHosts(preWarmSize)
+ }
+
+ private fun preWarmViewHosts(preWarmSize: Int) {
+ mainScope.launch {
+ // Applying isn't needed, as the surface was never actually shown.
+ val t = SurfaceControl.Transaction()
+ repeat(preWarmSize) {
+ val warmedViewHost = newInstance(context, context.display).apply { warmUp() }
+ // Put the warmed view host in the pool by releasing it.
+ release(warmedViewHost, t)
+ }
+ }
+ }
+
+ override fun acquire(context: Context, display: Display): WindowDecorViewHost {
+ val pooledViewHost = pool.acquire()
+ if (pooledViewHost != null) {
+ return pooledViewHost
+ }
+ Trace.beginSection("PooledWindowDecorViewHostSupplier#acquire-newInstance")
+ val newDecorViewHost = newInstance(context, display)
+ Trace.endSection()
+ return newDecorViewHost
+ }
+
+ override fun release(viewHost: WindowDecorViewHost, t: SurfaceControl.Transaction) {
+ val pooled = pool.release(viewHost)
+ if (!pooled) {
+ viewHost.release(t)
+ }
+ }
+
+ private fun newInstance(context: Context, display: Display): ReusableWindowDecorViewHost {
+ // Use a reusable window decor view host, as it allows swapping the entire view hierarchy.
+ return ReusableWindowDecorViewHost(
+ context = context,
+ mainScope = mainScope,
+ display = display,
+ id = nextDecorViewHostId++,
+ )
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHost.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHost.kt
new file mode 100644
index 0000000..da41e1b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHost.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.wm.shell.windowdecor.common.viewhost
+
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.PixelFormat
+import android.graphics.Region
+import android.view.Display
+import android.view.SurfaceControl
+import android.view.View
+import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+import android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+import android.view.WindowManager.LayoutParams.TYPE_APPLICATION
+import android.widget.FrameLayout
+import androidx.tracing.Trace
+import com.android.internal.annotations.VisibleForTesting
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+
+/**
+ * An implementation of [WindowDecorViewHost] that supports:
+ * 1) Replacing the root [View], meaning [WindowDecorViewHost.updateView] maybe be called with
+ * different [View] instances. This is useful when reusing [WindowDecorViewHost]s instances for
+ * vastly different view hierarchies, such as Desktop Windowing's App Handles and App Headers.
+ * 2) Pre-warming of the underlying [SurfaceControlViewHostAdapter]s. Useful because their creation
+ * and first root view assignment are expensive, which is undesirable in latency-sensitive code
+ * paths like during a shell transition.
+ */
+class ReusableWindowDecorViewHost(
+ private val context: Context,
+ @ShellMainThread private val mainScope: CoroutineScope,
+ display: Display,
+ val id: Int,
+ @VisibleForTesting
+ val viewHostAdapter: SurfaceControlViewHostAdapter =
+ SurfaceControlViewHostAdapter(context, display),
+) : WindowDecorViewHost, Warmable {
+ @VisibleForTesting val rootView = FrameLayout(context)
+
+ private var currentUpdateJob: Job? = null
+
+ override val surfaceControl: SurfaceControl
+ get() = viewHostAdapter.rootSurface
+
+ override fun warmUp() {
+ if (viewHostAdapter.isInitialized()) {
+ // Already warmed up.
+ return
+ }
+ Trace.beginSection("$TAG#warmUp")
+ viewHostAdapter.prepareViewHost(context.resources.configuration, touchableRegion = null)
+ viewHostAdapter.updateView(
+ rootView,
+ WindowManager.LayoutParams(
+ 0 /* width*/,
+ 0 /* height */,
+ TYPE_APPLICATION,
+ FLAG_NOT_FOCUSABLE or FLAG_SPLIT_TOUCH,
+ PixelFormat.TRANSPARENT,
+ )
+ .apply {
+ setTitle("View root of $TAG#$id")
+ setTrustedOverlay()
+ },
+ )
+ Trace.endSection()
+ }
+
+ override fun updateView(
+ view: View,
+ attrs: WindowManager.LayoutParams,
+ configuration: Configuration,
+ touchableRegion: Region?,
+ onDrawTransaction: SurfaceControl.Transaction?,
+ ) {
+ Trace.beginSection("ReusableWindowDecorViewHost#updateView")
+ clearCurrentUpdateJob()
+ updateViewHost(view, attrs, configuration, touchableRegion, onDrawTransaction)
+ Trace.endSection()
+ }
+
+ override fun updateViewAsync(
+ view: View,
+ attrs: WindowManager.LayoutParams,
+ configuration: Configuration,
+ touchableRegion: Region?,
+ ) {
+ Trace.beginSection("ReusableWindowDecorViewHost#updateViewAsync")
+ clearCurrentUpdateJob()
+ currentUpdateJob =
+ mainScope.launch {
+ updateViewHost(
+ view,
+ attrs,
+ configuration,
+ touchableRegion,
+ onDrawTransaction = null,
+ )
+ }
+ Trace.endSection()
+ }
+
+ override fun release(t: SurfaceControl.Transaction) {
+ clearCurrentUpdateJob()
+ viewHostAdapter.release(t)
+ }
+
+ private fun updateViewHost(
+ view: View,
+ attrs: WindowManager.LayoutParams,
+ configuration: Configuration,
+ touchableRegion: Region?,
+ onDrawTransaction: SurfaceControl.Transaction?,
+ ) {
+ Trace.beginSection("ReusableWindowDecorViewHost#updateViewHost")
+ viewHostAdapter.prepareViewHost(configuration, touchableRegion)
+ onDrawTransaction?.let { viewHostAdapter.applyTransactionOnDraw(it) }
+ rootView.removeAllViews()
+ rootView.addView(view)
+ viewHostAdapter.updateView(rootView, attrs)
+ Trace.endSection()
+ }
+
+ private fun clearCurrentUpdateJob() {
+ currentUpdateJob?.cancel()
+ currentUpdateJob = null
+ }
+
+ companion object {
+ private const val TAG = "ReusableWindowDecorViewHost"
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/SurfaceControlViewHostAdapter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/SurfaceControlViewHostAdapter.kt
new file mode 100644
index 0000000..26a43f4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/SurfaceControlViewHostAdapter.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.
+ */
+package com.android.wm.shell.windowdecor.common.viewhost
+
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Region
+import android.view.AttachedSurfaceControl
+import android.view.Display
+import android.view.SurfaceControl
+import android.view.SurfaceControlViewHost
+import android.view.View
+import android.view.WindowManager
+import android.view.WindowlessWindowManager
+import androidx.tracing.Trace
+import com.android.internal.annotations.VisibleForTesting
+
+typealias SurfaceControlViewHostFactory =
+ (Context, Display, WindowlessWindowManager, String) -> SurfaceControlViewHost
+
+/**
+ * Adapter for a [SurfaceControlViewHost] and its backing [SurfaceControl].
+ *
+ * It does not support swapping the root view added to the VRI of the [SurfaceControlViewHost], and
+ * any attempts to do will throw, which means that once a [View] is added using [updateView], only
+ * its properties and binding may be changed, children views may be added, removed or changed
+ * and its [WindowManager.LayoutParams] may be changed.
+ */
+class SurfaceControlViewHostAdapter(
+ private val context: Context,
+ private val display: Display,
+ private val surfaceControlViewHostFactory: SurfaceControlViewHostFactory = { c, d, wwm, s ->
+ SurfaceControlViewHost(c, d, wwm, s)
+ },
+) {
+ val rootSurface: SurfaceControl =
+ SurfaceControl.Builder()
+ .setName("SurfaceControlViewHostAdapter surface")
+ .setContainerLayer()
+ .setCallsite("SurfaceControlViewHostAdapter#init")
+ .build()
+
+ private var wwm: WindowDecorWindowlessWindowManager? = null
+ @VisibleForTesting var viewHost: SurfaceControlViewHost? = null
+
+ /**
+ * Initialize or updates the [SurfaceControlViewHost].
+ */
+ fun prepareViewHost(
+ configuration: Configuration,
+ touchableRegion: Region?
+ ) {
+ if (wwm == null) {
+ wwm = WindowDecorWindowlessWindowManager(configuration, rootSurface)
+ }
+ if (viewHost == null) {
+ viewHost =
+ surfaceControlViewHostFactory.invoke(
+ context,
+ display,
+ requireWindowlessWindowManager(),
+ "SurfaceControlViewHostAdapter#prepareViewHost",
+ )
+ }
+ requireWindowlessWindowManager().setConfiguration(configuration)
+ requireWindowlessWindowManager().setTouchRegion(requireViewHost(), touchableRegion)
+ }
+
+ /**
+ * Request to apply the transaction atomically with the next draw of the view hierarchy. See
+ * [AttachedSurfaceControl.applyTransactionOnDraw].
+ */
+ fun applyTransactionOnDraw(t: SurfaceControl.Transaction) {
+ requireViewHost().rootSurfaceControl.applyTransactionOnDraw(t)
+ }
+
+ /** Update the view hierarchy of the view host. */
+ fun updateView(view: View, attrs: WindowManager.LayoutParams) {
+ if (requireViewHost().view == null) {
+ Trace.beginSection("SurfaceControlViewHostAdapter#updateView-setView")
+ requireViewHost().setView(view, attrs)
+ Trace.endSection()
+ } else {
+ check(requireViewHost().view == view) { "Changing view is not allowed" }
+ Trace.beginSection("SurfaceControlViewHostAdapter#updateView-relayout")
+ requireViewHost().relayout(attrs)
+ Trace.endSection()
+ }
+ }
+
+ /** Release the view host and remove the backing surface. */
+ fun release(t: SurfaceControl.Transaction) {
+ viewHost?.release()
+ t.remove(rootSurface)
+ }
+
+ /** Whether the view host has had a view hierarchy set. */
+ fun isInitialized(): Boolean = viewHost?.view != null
+
+ private fun requireWindowlessWindowManager(): WindowDecorWindowlessWindowManager {
+ return wwm ?: error("Expected non-null windowless window manager")
+ }
+
+ private fun requireViewHost(): SurfaceControlViewHost {
+ return viewHost ?: error("Expected non-null view host")
+ }
+}
diff --git a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/Warmable.kt
similarity index 71%
copy from core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
copy to libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/Warmable.kt
index 0589bf8..2cb0f89 100644
--- a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/Warmable.kt
@@ -5,7 +5,7 @@
* you may not use this 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,
@@ -13,10 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package android.app.ondeviceintelligence;
+package com.android.wm.shell.windowdecor.common.viewhost
/**
- * @hide
- */
-parcelable FeatureDetails;
+ * An interface for an object that can be warmed up before it's needed.
+ */
+interface Warmable {
+ fun warmUp()
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorViewHost.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorViewHost.kt
index 7c1479e..2dcbbac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorViewHost.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorViewHost.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.windowdecor.common.viewhost
import android.content.res.Configuration
+import android.graphics.Region
import android.view.SurfaceControl
import android.view.View
import android.view.WindowManager
@@ -34,11 +35,17 @@
view: View,
attrs: WindowManager.LayoutParams,
configuration: Configuration,
- onDrawTransaction: SurfaceControl.Transaction?,
+ touchableRegion: Region? = null,
+ onDrawTransaction: SurfaceControl.Transaction? = null,
)
/** Asynchronously update the view hierarchy of this view host. */
- fun updateViewAsync(view: View, attrs: WindowManager.LayoutParams, configuration: Configuration)
+ fun updateViewAsync(
+ view: View,
+ attrs: WindowManager.LayoutParams,
+ configuration: Configuration,
+ touchableRegion: Region? = null,
+ )
/** Releases the underlying [View] hierarchy and removes the backing [SurfaceControl]. */
fun release(t: SurfaceControl.Transaction)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorWindowlessWindowManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorWindowlessWindowManager.kt
new file mode 100644
index 0000000..fbe8c6c8
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorWindowlessWindowManager.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.wm.shell.windowdecor.common.viewhost
+
+import android.content.res.Configuration
+import android.graphics.Region
+import android.view.SurfaceControl
+import android.view.SurfaceControlViewHost
+import android.view.WindowlessWindowManager
+
+/**
+ * A [WindowlessWindowManager] for the window decor caption that allows customizing the touchable
+ * region.
+ */
+class WindowDecorWindowlessWindowManager(
+ configuration: Configuration,
+ rootSurface: SurfaceControl,
+) : WindowlessWindowManager(configuration, rootSurface, /* hostInputTransferToken= */ null) {
+
+ /** Set the view host's touchable region. */
+ fun setTouchRegion(viewHost: SurfaceControlViewHost, region: Region?) {
+ setTouchRegion(viewHost.windowToken.asBinder(), region)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
index f6d2cc0..f4f60d7 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
@@ -272,9 +272,11 @@
TaggedCujTransitionMatcher(associatedTransitionRequired = false)
)
.build(),
- assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
- listOf(AppWindowCoversLeftHalfScreenAtEnd(DESKTOP_MODE_APP))
- .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + listOf(
+ AppWindowCoversLeftHalfScreenAtEnd(
+ DESKTOP_MODE_APP, SNAP_WINDOW_MAX_DIFF_THRESHOLD_RATIO
+ )
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
)
val SNAP_RESIZE_RIGHT_WITH_DRAG =
@@ -287,9 +289,11 @@
TaggedCujTransitionMatcher(associatedTransitionRequired = false)
)
.build(),
- assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
- listOf(AppWindowCoversRightHalfScreenAtEnd(DESKTOP_MODE_APP))
- .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + listOf(
+ AppWindowCoversRightHalfScreenAtEnd(
+ DESKTOP_MODE_APP, SNAP_WINDOW_MAX_DIFF_THRESHOLD_RATIO
+ )
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
)
val SNAP_RESIZE_WITH_DRAG_NON_RESIZABLE =
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromAnotherApp.kt
index 2ccffa85..a3d6020 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromAnotherApp.kt
@@ -66,5 +66,6 @@
fun teardown() {
primaryApp.exit(wmHelper)
secondaryApp.exit(wmHelper)
+ Utils.resetFreezeRecentTaskList()
}
}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromHome.kt
index 8673c46..9c7de05 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromHome.kt
@@ -65,5 +65,6 @@
fun teardown() {
primaryApp.exit(wmHelper)
secondaryApp.exit(wmHelper)
+ Utils.resetFreezeRecentTaskList()
}
}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromRecent.kt
index 22adf6c..9eb2972 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromRecent.kt
@@ -68,5 +68,6 @@
fun teardown() {
primaryApp.exit(wmHelper)
secondaryApp.exit(wmHelper)
+ Utils.resetFreezeRecentTaskList()
}
}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBetweenSplitPairs.kt
index 4ded148..d833d91 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBetweenSplitPairs.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBetweenSplitPairs.kt
@@ -68,5 +68,6 @@
secondaryApp.exit(wmHelper)
thirdApp.exit(wmHelper)
fourthApp.exit(wmHelper)
+ Utils.resetFreezeRecentTaskList()
}
}
diff --git a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt
index c0fafef..4a9e73b 100644
--- a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt
+++ b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt
@@ -28,7 +28,10 @@
import android.tools.flicker.rules.ChangeDisplayOrientationRule
import android.tools.flicker.rules.LaunchAppRule
import android.tools.flicker.rules.RemoveAllTasksButHomeRule
+import android.util.Log
import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import java.io.IOException
import org.junit.rules.RuleChain
object Utils {
@@ -52,4 +55,17 @@
.around(PressHomeRule())
.around(EnsureDeviceSettingsRule())
}
+
+ /**
+ * Resets the frozen recent tasks list (ie. commits the quickswitch to the current task and
+ * reorders the current task to the end of the recents list).
+ */
+ fun resetFreezeRecentTaskList() {
+ try {
+ UiDevice.getInstance(instrumentation)
+ .executeShellCommand("wm reset-freeze-recent-tasks")
+ } catch (e: IOException) {
+ Log.e("TestUtils", "Failed to reset frozen recent tasks list", e)
+ }
+ }
}
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 310c2d7..ec3fe95 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
@@ -54,6 +54,7 @@
private final Point mPositionInParent = new Point();
private boolean mIsVisible = false;
private boolean mIsTopActivityTransparent = false;
+ private boolean mIsActivityStackTransparent = false;
private int mNumActivities = 1;
private long mLastActiveTime;
@@ -158,6 +159,12 @@
return this;
}
+ public TestRunningTaskInfoBuilder setActivityStackTransparent(
+ boolean isActivityStackTransparent) {
+ mIsActivityStackTransparent = isActivityStackTransparent;
+ return this;
+ }
+
public TestRunningTaskInfoBuilder setNumActivities(int numActivities) {
mNumActivities = numActivities;
return this;
@@ -187,6 +194,7 @@
info.positionInParent = mPositionInParent;
info.isVisible = mIsVisible;
info.isTopActivityTransparent = mIsTopActivityTransparent;
+ info.isActivityStackTransparent = mIsActivityStackTransparent;
info.numActivities = mNumActivities;
info.lastActiveTime = mLastActiveTime;
info.userId = mUserId;
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 f22e2a5..a2afd2c 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
@@ -33,6 +33,8 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -634,7 +636,7 @@
releaseBackGesture();
mShellExecutor.flushAll();
- verify(mAppCallback).setHandoffHandler(any());
+ verify(mAppCallback).setHandoffHandler(notNull());
}
@Test
@@ -654,7 +656,7 @@
releaseBackGesture();
mShellExecutor.flushAll();
- verify(mAppCallback, never()).setHandoffHandler(any());
+ verify(mAppCallback).setHandoffHandler(isNull());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java
index 329a109..bf03834 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java
@@ -15,9 +15,12 @@
*/
package com.android.wm.shell.bubbles.bar;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import android.graphics.Color;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -61,4 +64,18 @@
assertEquals(handleColor,
ContextCompat.getColor(mContext, R.color.bubble_bar_expanded_view_handle_light));
}
+
+ @Test
+ public void testSetHandleInitialColor_beforeUpdateHandleColor_updatesColor() {
+ mHandleView.setHandleInitialColor(Color.RED);
+ assertThat(mHandleView.getHandleColor()).isEqualTo(Color.RED);
+ }
+
+ @Test
+ public void testSetHandleInitialColor_afterUpdateHandleColor_doesNotUpdateColor() {
+ mHandleView.updateHandleColor(/* isRegionDark= */ true, /* animated= */ false);
+ mHandleView.setHandleInitialColor(Color.RED);
+ assertThat(mHandleView.getHandleColor()).isEqualTo(
+ ContextCompat.getColor(mContext, R.color.bubble_bar_expanded_view_handle_light));
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/HandlerExecutorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/HandlerExecutorTest.kt
new file mode 100644
index 0000000..799b48c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/HandlerExecutorTest.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.common
+
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.Looper
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.wm.shell.ShellTestCase
+import com.google.common.truth.Truth.assertThat
+import java.util.function.BiConsumer
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.MockitoSession
+import org.mockito.kotlin.whenever
+
+/**
+ * Tests for HandlerExecutor.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:HandlerExecutorTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class HandlerExecutorTest : ShellTestCase() {
+
+ class TestSetThreadPriorityFn : BiConsumer<Int, Int> {
+ var lastSetPriority = UNSET_THREAD_PRIORITY
+ private set
+ var callCount = 0
+ private set
+
+ override fun accept(tid: Int, priority: Int) {
+ lastSetPriority = priority
+ callCount++
+ }
+
+ fun reset() {
+ lastSetPriority = UNSET_THREAD_PRIORITY
+ callCount = 0
+ }
+ }
+
+ val testSetPriorityFn = TestSetThreadPriorityFn()
+
+ @Test
+ fun defaultExecutorDisallowBoost() {
+ val executor = createTestHandlerExecutor()
+
+ executor.setBoost()
+
+ assertThat(executor.isBoosted()).isFalse()
+ }
+
+ @Test
+ fun boostExecutor_resetWhenNotSet_expectNoOp() {
+ val executor = createTestHandlerExecutor(DEFAULT_THREAD_PRIORITY, BOOSTED_THREAD_PRIORITY)
+ val mockSession: MockitoSession = ExtendedMockito.mockitoSession()
+ .mockStatic(android.os.Process::class.java)
+ .startMocking()
+
+ try {
+ // Try to reset and ensure we never try to set the thread priority
+ executor.resetBoost()
+
+ assertThat(testSetPriorityFn.callCount).isEqualTo(0)
+ assertThat(executor.isBoosted()).isFalse()
+ } finally {
+ mockSession.finishMocking()
+ }
+ }
+
+ @Test
+ fun boostExecutor_setResetBoost_expectThreadPriorityUpdated() {
+ val executor = createTestHandlerExecutor(DEFAULT_THREAD_PRIORITY, BOOSTED_THREAD_PRIORITY)
+ val mockSession: MockitoSession = ExtendedMockito.mockitoSession()
+ .mockStatic(android.os.Process::class.java)
+ .startMocking()
+
+ try {
+ // Boost and ensure the boosted thread priority is requested
+ executor.setBoost()
+
+ assertThat(testSetPriorityFn.lastSetPriority).isEqualTo(BOOSTED_THREAD_PRIORITY)
+ assertThat(testSetPriorityFn.callCount).isEqualTo(1)
+ assertThat(executor.isBoosted()).isTrue()
+
+ // Reset and ensure the default thread priority is requested
+ executor.resetBoost()
+
+ assertThat(testSetPriorityFn.lastSetPriority).isEqualTo(DEFAULT_THREAD_PRIORITY)
+ assertThat(testSetPriorityFn.callCount).isEqualTo(2)
+ assertThat(executor.isBoosted()).isFalse()
+ } finally {
+ mockSession.finishMocking()
+ }
+ }
+
+ @Test
+ fun boostExecutor_overlappingBoost_expectResetOnlyWhenNotOverlapping() {
+ val executor = createTestHandlerExecutor(DEFAULT_THREAD_PRIORITY, BOOSTED_THREAD_PRIORITY)
+ val mockSession: MockitoSession = ExtendedMockito.mockitoSession()
+ .mockStatic(android.os.Process::class.java)
+ .startMocking()
+
+ try {
+ // Set and ensure we only update the thread priority once
+ executor.setBoost()
+ executor.setBoost()
+
+ assertThat(testSetPriorityFn.lastSetPriority).isEqualTo(BOOSTED_THREAD_PRIORITY)
+ assertThat(testSetPriorityFn.callCount).isEqualTo(1)
+ assertThat(executor.isBoosted()).isTrue()
+
+ // Reset and ensure we are still boosted and the thread priority doesn't change
+ executor.resetBoost()
+
+ assertThat(testSetPriorityFn.lastSetPriority).isEqualTo(BOOSTED_THREAD_PRIORITY)
+ assertThat(testSetPriorityFn.callCount).isEqualTo(1)
+ assertThat(executor.isBoosted()).isTrue()
+
+ // Reset again and ensure we update the thread priority accordingly
+ executor.resetBoost()
+
+ assertThat(testSetPriorityFn.lastSetPriority).isEqualTo(DEFAULT_THREAD_PRIORITY)
+ assertThat(testSetPriorityFn.callCount).isEqualTo(2)
+ assertThat(executor.isBoosted()).isFalse()
+ } finally {
+ mockSession.finishMocking()
+ }
+ }
+
+ /**
+ * Creates a test handler executor backed by a mocked handler thread.
+ */
+ private fun createTestHandlerExecutor(
+ defaultThreadPriority: Int = DEFAULT_THREAD_PRIORITY,
+ boostedThreadPriority: Int = DEFAULT_THREAD_PRIORITY
+ ) : HandlerExecutor {
+ val handler = mock(Handler::class.java)
+ val looper = mock(Looper::class.java)
+ val thread = mock(HandlerThread::class.java)
+ whenever(handler.looper).thenReturn(looper)
+ whenever(looper.thread).thenReturn(thread)
+ whenever(thread.threadId).thenReturn(1234)
+ val executor = HandlerExecutor(handler, defaultThreadPriority, boostedThreadPriority)
+ executor.replaceSetThreadPriorityFn(testSetPriorityFn)
+ return executor
+ }
+
+ companion object {
+ private const val UNSET_THREAD_PRIORITY = 0
+ private const val DEFAULT_THREAD_PRIORITY = 1
+ private const val BOOSTED_THREAD_PRIORITY = 1000
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt
index 1d39000..d52fd4f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt
@@ -37,35 +37,46 @@
@SmallTest
class AppCompatUtilsTest : ShellTestCase() {
@Test
- fun testIsTopActivityExemptFromDesktopWindowing_topActivityTransparent() {
+ fun testIsTopActivityExemptFromDesktopWindowing_onlyTransparentActivitiesInStack() {
assertTrue(isTopActivityExemptFromDesktopWindowing(mContext,
createFreeformTask(/* displayId */ 0)
.apply {
- isTopActivityTransparent = true
- numActivities = 1
+ isActivityStackTransparent = true
isTopActivityNoDisplay = false
+ numActivities = 1
}))
}
@Test
- fun testIsTopActivityExemptFromDesktopWindowing_topActivityTransparent_multipleActivities() {
+ fun testIsTopActivityExemptFromDesktopWindowing_noActivitiesInStack() {
assertFalse(isTopActivityExemptFromDesktopWindowing(mContext,
createFreeformTask(/* displayId */ 0)
.apply {
- isTopActivityTransparent = true
- numActivities = 2
+ isActivityStackTransparent = true
isTopActivityNoDisplay = false
+ numActivities = 0
}))
}
@Test
- fun testIsTopActivityExemptFromDesktopWindowing_topActivityTransparent_notDisplayed() {
+ fun testIsTopActivityExemptFromDesktopWindowing_nonTransparentActivitiesInStack() {
assertFalse(isTopActivityExemptFromDesktopWindowing(mContext,
createFreeformTask(/* displayId */ 0)
.apply {
- isTopActivityTransparent = true
+ isActivityStackTransparent = false
+ isTopActivityNoDisplay = false
numActivities = 1
+ }))
+ }
+
+ @Test
+ fun testIsTopActivityExemptFromDesktopWindowing_transparentActivityStack_notDisplayed() {
+ assertFalse(isTopActivityExemptFromDesktopWindowing(mContext,
+ createFreeformTask(/* displayId */ 0)
+ .apply {
+ isActivityStackTransparent = true
isTopActivityNoDisplay = true
+ numActivities = 1
}))
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduWindowManagerTest.java
index 94dbd11..4c97c76 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/LetterboxEduWindowManagerTest.java
@@ -19,6 +19,7 @@
import static android.content.res.Configuration.UI_MODE_NIGHT_YES;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.window.flags.Flags.FLAG_APP_COMPAT_ASYNC_RELAYOUT;
import static com.android.window.flags.Flags.FLAG_APP_COMPAT_UI_FRAMEWORK;
import static com.android.wm.shell.compatui.CompatUIStatusManager.COMPAT_UI_EDUCATION_HIDDEN;
import static com.android.wm.shell.compatui.CompatUIStatusManager.COMPAT_UI_EDUCATION_VISIBLE;
@@ -42,10 +43,12 @@
import android.app.TaskInfo;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
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.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.util.Pair;
import android.view.DisplayCutout;
@@ -125,6 +128,9 @@
@Mock private Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnDismissCallback;
@Mock private DockStateReader mDockStateReader;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private CompatUIConfiguration mCompatUIConfiguration;
private TestShellExecutor mExecutor;
private FakeCompatUIStatusManagerTest mCompatUIStatus;
@@ -317,6 +323,7 @@
@Test
@RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
+ @DisableFlags(FLAG_APP_COMPAT_ASYNC_RELAYOUT)
public void testUpdateCompatInfo_updatesLayoutCorrectly() {
LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
@@ -346,6 +353,36 @@
@Test
@RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
+ @EnableFlags(FLAG_APP_COMPAT_ASYNC_RELAYOUT)
+ public void testUpdateCompatInfo_updatesLayoutCorrectlyAsync() {
+ LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+ assertTrue(windowManager.createLayout(/* canShow= */ true));
+ LetterboxEduDialogLayout layout = windowManager.mLayout;
+ assertNotNull(layout);
+
+ assertTrue(windowManager.updateCompatInfo(
+ createTaskInfo(/* eligible= */ true, USER_ID_1, new Rect(50, 25, 150, 75)),
+ mTaskListener, /* canShow= */ true));
+
+ verifyLayout(layout, layout.getLayoutParams(), /* expectedWidth= */ 100,
+ /* expectedHeight= */ 50, /* expectedExtraTopMargin= */ 0,
+ /* expectedExtraBottomMargin= */ 0);
+ verify(mViewHost).relayout(mWindowAttrsCaptor.capture(), any());
+ assertThat(mWindowAttrsCaptor.getValue()).isEqualTo(layout.getLayoutParams());
+
+ // Window manager should be released (without animation) when eligible becomes false.
+ assertFalse(windowManager.updateCompatInfo(createTaskInfo(/* eligible= */ false),
+ mTaskListener, /* canShow= */ true));
+
+ verify(windowManager).release();
+ verify(mOnDismissCallback, never()).accept(any());
+ verify(mAnimationController, never()).startExitAnimation(any(), any());
+ assertNull(windowManager.mLayout);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
public void testUpdateCompatInfo_notEligibleUntilUpdate_createsLayoutAfterUpdate() {
LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ false);
@@ -375,6 +412,7 @@
@Test
@RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
+ @DisableFlags(FLAG_APP_COMPAT_ASYNC_RELAYOUT)
public void testUpdateDisplayLayout_updatesLayoutCorrectly() {
LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
@@ -397,6 +435,29 @@
@Test
@RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
+ @EnableFlags(FLAG_APP_COMPAT_ASYNC_RELAYOUT)
+ public void testUpdateDisplayLayout_updatesLayoutCorrectlyAsync() {
+ LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+ assertTrue(windowManager.createLayout(/* canShow= */ true));
+ LetterboxEduDialogLayout layout = windowManager.mLayout;
+ assertNotNull(layout);
+
+ int newDisplayCutoutTop = DISPLAY_CUTOUT_TOP + 7;
+ int newDisplayCutoutBottom = DISPLAY_CUTOUT_BOTTOM + 9;
+ windowManager.updateDisplayLayout(createDisplayLayout(
+ Insets.of(DISPLAY_CUTOUT_HORIZONTAL, newDisplayCutoutTop,
+ DISPLAY_CUTOUT_HORIZONTAL, newDisplayCutoutBottom)));
+
+ verifyLayout(layout, layout.getLayoutParams(), /* expectedWidth= */ TASK_WIDTH,
+ /* expectedHeight= */ TASK_HEIGHT, /* expectedExtraTopMargin= */
+ newDisplayCutoutTop, /* expectedExtraBottomMargin= */ newDisplayCutoutBottom);
+ verify(mViewHost).relayout(mWindowAttrsCaptor.capture(), any());
+ assertThat(mWindowAttrsCaptor.getValue()).isEqualTo(layout.getLayoutParams());
+ }
+
+ @Test
+ @RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
public void testRelease_animationIsCancelled() {
LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxConfigurationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxConfigurationTest.kt
index 75025d90..1399600 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxConfigurationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxConfigurationTest.kt
@@ -26,6 +26,7 @@
import com.android.internal.R
import com.android.wm.shell.ShellTestCase
import java.util.function.Consumer
+import kotlin.test.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
@@ -51,6 +52,14 @@
val COLOR_WHITE_RESOURCE_ID = android.R.color.white
@JvmStatic
val COLOR_BLACK_RESOURCE_ID = android.R.color.black
+ @JvmStatic
+ val ROUNDED_CORNER_RADIUS_DEFAULT = 32
+ @JvmStatic
+ val ROUNDED_CORNER_RADIUS_VALID = 16
+ @JvmStatic
+ val ROUNDED_CORNER_RADIUS_NONE = 0
+ @JvmStatic
+ val ROUNDED_CORNER_RADIUS_INVALID = -10
}
@Test
@@ -112,6 +121,68 @@
}
}
+ @Test
+ fun `default rounded corner radius is used if override is not set`() {
+ runTestScenario { r ->
+ r.setDefaultRoundedCornerRadius(ROUNDED_CORNER_RADIUS_DEFAULT)
+ r.loadConfiguration()
+ r.checkRoundedCornersRadius(ROUNDED_CORNER_RADIUS_DEFAULT)
+ }
+ }
+
+ @Test
+ fun `new rounded corner radius is used after setting a valid value`() {
+ runTestScenario { r ->
+ r.setDefaultRoundedCornerRadius(ROUNDED_CORNER_RADIUS_DEFAULT)
+ r.loadConfiguration()
+ r.overrideRoundedCornersRadius(ROUNDED_CORNER_RADIUS_VALID)
+ r.checkRoundedCornersRadius(ROUNDED_CORNER_RADIUS_VALID)
+ }
+ }
+
+ @Test
+ fun `no rounded corner radius is used after setting an invalid value`() {
+ runTestScenario { r ->
+ r.setDefaultRoundedCornerRadius(ROUNDED_CORNER_RADIUS_DEFAULT)
+ r.loadConfiguration()
+ r.overrideRoundedCornersRadius(ROUNDED_CORNER_RADIUS_INVALID)
+ r.checkRoundedCornersRadius(ROUNDED_CORNER_RADIUS_NONE)
+ }
+ }
+
+ @Test
+ fun `has rounded corners for different values`() {
+ runTestScenario { r ->
+ r.setDefaultRoundedCornerRadius(ROUNDED_CORNER_RADIUS_DEFAULT)
+ r.loadConfiguration()
+ r.checkIsLetterboxActivityCornersRounded(true)
+
+ r.overrideRoundedCornersRadius(ROUNDED_CORNER_RADIUS_INVALID)
+ r.checkIsLetterboxActivityCornersRounded(false)
+
+ r.overrideRoundedCornersRadius(ROUNDED_CORNER_RADIUS_NONE)
+ r.checkIsLetterboxActivityCornersRounded(false)
+
+ r.overrideRoundedCornersRadius(ROUNDED_CORNER_RADIUS_VALID)
+ r.checkIsLetterboxActivityCornersRounded(true)
+ }
+ }
+
+ @Test
+ fun `reset rounded corners radius`() {
+ runTestScenario { r ->
+ r.setDefaultRoundedCornerRadius(ROUNDED_CORNER_RADIUS_DEFAULT)
+ r.loadConfiguration()
+ r.checkRoundedCornersRadius(ROUNDED_CORNER_RADIUS_DEFAULT)
+
+ r.overrideRoundedCornersRadius(ROUNDED_CORNER_RADIUS_VALID)
+ r.checkRoundedCornersRadius(ROUNDED_CORNER_RADIUS_VALID)
+
+ r.resetRoundedCornersRadius()
+ r.checkRoundedCornersRadius(ROUNDED_CORNER_RADIUS_DEFAULT)
+ }
+ }
+
/**
* Runs a test scenario providing a Robot.
*/
@@ -135,6 +206,11 @@
.getColor(R.color.config_letterboxBackgroundColor, null)
}
+ fun setDefaultRoundedCornerRadius(radius: Int) {
+ doReturn(radius).`when`(resources)
+ .getInteger(R.integer.config_letterboxActivityCornersRadius)
+ }
+
fun loadConfiguration() {
letterboxConfig = LetterboxConfiguration(ctx)
}
@@ -147,14 +223,30 @@
letterboxConfig.resetLetterboxBackgroundColor()
}
+ fun resetRoundedCornersRadius() {
+ letterboxConfig.resetLetterboxActivityCornersRadius()
+ }
+
fun overrideBackgroundColorId(@ColorRes colorId: Int) {
letterboxConfig.setLetterboxBackgroundColorResourceId(colorId)
}
+ fun overrideRoundedCornersRadius(radius: Int) {
+ letterboxConfig.setLetterboxActivityCornersRadius(radius)
+ }
+
fun checkBackgroundColor(expected: Color) {
val colorComponents = letterboxConfig.getBackgroundColorRgbArray()
val expectedComponents = expected.components
assert(expectedComponents.contentEquals(colorComponents))
}
+
+ fun checkRoundedCornersRadius(expected: Int) {
+ assertEquals(expected, letterboxConfig.getLetterboxActivityCornersRadius())
+ }
+
+ fun checkIsLetterboxActivityCornersRounded(expected: Boolean) {
+ assertEquals(expected, letterboxConfig.isLetterboxActivityCornersRounded())
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerStrategyTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerStrategyTest.kt
new file mode 100644
index 0000000..50fdf45
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerStrategyTest.kt
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+package com.android.wm.shell.compatui.letterbox
+
+import android.content.Context
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.compatui.letterbox.LetterboxControllerStrategy.LetterboxMode.MULTIPLE_SURFACES
+import com.android.wm.shell.compatui.letterbox.LetterboxControllerStrategy.LetterboxMode.SINGLE_SURFACE
+import java.util.function.Consumer
+import kotlin.test.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for [LetterboxControllerStrategy].
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:LetterboxControllerStrategyTest
+ */
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class LetterboxControllerStrategyTest : ShellTestCase() {
+
+ @Test
+ fun `LetterboxMode is MULTIPLE_SURFACES with rounded corners`() {
+ runTestScenario { r ->
+ r.configureRoundedCornerRadius(true)
+ r.configureLetterboxMode()
+ r.checkLetterboxModeIsSingle()
+ }
+ }
+
+ @Test
+ fun `LetterboxMode is MULTIPLE_SURFACES with no rounded corners`() {
+ runTestScenario { r ->
+ r.configureRoundedCornerRadius(false)
+ r.configureLetterboxMode()
+ r.checkLetterboxModeIsMultiple()
+ }
+ }
+
+ /**
+ * Runs a test scenario providing a Robot.
+ */
+ fun runTestScenario(consumer: Consumer<LetterboxStrategyRobotTest>) {
+ val robot = LetterboxStrategyRobotTest(mContext)
+ consumer.accept(robot)
+ }
+
+ class LetterboxStrategyRobotTest(val ctx: Context) {
+
+ companion object {
+ @JvmStatic
+ private val ROUNDED_CORNERS_TRUE = 10
+ @JvmStatic
+ private val ROUNDED_CORNERS_FALSE = 0
+ }
+
+ private val letterboxConfiguration: LetterboxConfiguration
+ private val letterboxStrategy: LetterboxControllerStrategy
+
+ init {
+ letterboxConfiguration = LetterboxConfiguration(ctx)
+ letterboxStrategy = LetterboxControllerStrategy(letterboxConfiguration)
+ }
+
+ fun configureRoundedCornerRadius(enabled: Boolean) {
+ letterboxConfiguration.setLetterboxActivityCornersRadius(
+ if (enabled) ROUNDED_CORNERS_TRUE else ROUNDED_CORNERS_FALSE
+ )
+ }
+
+ fun configureLetterboxMode() {
+ letterboxStrategy.configureLetterboxMode()
+ }
+
+ fun checkLetterboxModeIsSingle(expected: Boolean = true) {
+ val expectedMode = if (expected) SINGLE_SURFACE else MULTIPLE_SURFACES
+ assertEquals(expectedMode, letterboxStrategy.getLetterboxImplementationMode())
+ }
+
+ fun checkLetterboxModeIsMultiple(expected: Boolean = true) {
+ val expectedMode = if (expected) MULTIPLE_SURFACES else SINGLE_SURFACE
+ assertEquals(expectedMode, letterboxStrategy.getLetterboxImplementationMode())
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxUtilsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxUtilsTest.kt
index 06b8052..6675112 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxUtilsTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxUtilsTest.kt
@@ -19,9 +19,14 @@
import android.content.Context
import android.graphics.Rect
import android.testing.AndroidTestingRunner
+import android.view.SurfaceControl
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.compatui.letterbox.LetterboxUtils.Maps.runOnItem
+import com.android.wm.shell.compatui.letterbox.LetterboxUtils.Transactions.moveAndCrop
import java.util.function.Consumer
+import kotlin.test.assertEquals
+import kotlin.test.assertNull
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
@@ -100,6 +105,35 @@
}
}
+ @Test
+ fun `runOnItem executes onFound when an item has been found for a key`() {
+ runTestScenario { r ->
+ r.initMap(1 to 2, 3 to 4)
+ r.runOnItem<Int>(1)
+ r.verifyOnItemInvoked(expectedItem = 2)
+ r.verifyOnMissingNotInvoked()
+ }
+ }
+
+ @Test
+ fun `runOnItem executes onMissing when an item has not been found for a key`() {
+ runTestScenario { r ->
+ r.initMap(1 to 2, 3 to 4)
+ r.runOnItem<Int>(8)
+ r.verifyOnItemNotInvoked()
+ r.verifyOnMissingInvoked(expectedKey = 8)
+ }
+ }
+
+ @Test
+ fun `moveAndCrop invoked Move and then Crop`() {
+ runTestScenario { r ->
+ r.invoke(Rect(1, 2, 51, 62))
+ r.verifySetPosition(expectedX = 1f, expectedY = 2f)
+ r.verifySetWindowCrop(expectedWidth = 50, expectedHeight = 60)
+ }
+ }
+
/**
* Runs a test scenario providing a Robot.
*/
@@ -113,6 +147,14 @@
builder: (LetterboxSurfaceBuilder) -> LetterboxController
) : LetterboxControllerRobotTest(ctx, builder) {
+ private var testableMap = mutableMapOf<Int, Int>()
+ private var onItemState: Int? = null
+ private var onMissingStateKey: Int? = null
+ private var onMissingStateMap: MutableMap<Int, Int>? = null
+
+ private val transaction = getTransactionMock()
+ private val surface = SurfaceControl()
+
fun verifyCreateSurfaceInvokedWithRequest(
target: LetterboxController,
times: Int = 1
@@ -147,5 +189,46 @@
) {
verify(target, times(times)).dump()
}
+
+ fun initMap(vararg values: Pair<Int, Int>) = testableMap.putAll(values.toMap())
+
+ fun <T> runOnItem(key: Int) {
+ testableMap.runOnItem(key, onFound = { item ->
+ onItemState = item
+ }, onMissed = { k, m ->
+ onMissingStateKey = k
+ onMissingStateMap = m
+ })
+ }
+
+ fun verifyOnItemInvoked(expectedItem: Int) {
+ assertEquals(expectedItem, onItemState)
+ }
+
+ fun verifyOnItemNotInvoked() {
+ assertNull(onItemState)
+ }
+
+ fun verifyOnMissingInvoked(expectedKey: Int) {
+ assertEquals(expectedKey, onMissingStateKey)
+ assertEquals(onMissingStateMap, testableMap)
+ }
+
+ fun verifyOnMissingNotInvoked() {
+ assertNull(onMissingStateKey)
+ assertNull(onMissingStateMap)
+ }
+
+ fun invoke(rect: Rect) {
+ transaction.moveAndCrop(surface, rect)
+ }
+
+ fun verifySetPosition(expectedX: Float, expectedY: Float) {
+ verify(transaction).setPosition(surface, expectedX, expectedY)
+ }
+
+ fun verifySetWindowCrop(expectedWidth: Int, expectedHeight: Int) {
+ verify(transaction).setWindowCrop(surface, expectedWidth, expectedHeight)
+ }
}
}
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 c10434a..7c9494c 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
@@ -21,6 +21,7 @@
import android.app.ActivityOptions
import android.app.KeyguardManager
import android.app.PendingIntent
+import android.app.PictureInPictureParams
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
@@ -1151,7 +1152,7 @@
fun moveRunningTaskToDesktop_topActivityTranslucentWithoutDisplay_taskIsMovedToDesktop() {
val task =
setUpFullscreenTask().apply {
- isTopActivityTransparent = true
+ isActivityStackTransparent = true
isTopActivityNoDisplay = true
numActivities = 1
}
@@ -1167,7 +1168,7 @@
fun moveRunningTaskToDesktop_topActivityTranslucentWithDisplay_doesNothing() {
val task =
setUpFullscreenTask().apply {
- isTopActivityTransparent = true
+ isActivityStackTransparent = true
isTopActivityNoDisplay = false
numActivities = 1
}
@@ -1724,6 +1725,34 @@
}
@Test
+ fun onDesktopWindowMinimize_pipTask_autoEnterEnabled_startPipTransition() {
+ val task = setUpPipTask(autoEnterEnabled = true)
+ val handler = mock(TransitionHandler::class.java)
+ whenever(freeformTaskTransitionStarter.startPipTransition(any()))
+ .thenReturn(Binder())
+ whenever(transitions.dispatchRequest(any(), any(), anyOrNull()))
+ .thenReturn(android.util.Pair(handler, WindowContainerTransaction())
+ )
+
+ controller.minimizeTask(task)
+
+ verify(freeformTaskTransitionStarter).startPipTransition(any())
+ verify(freeformTaskTransitionStarter, never()).startMinimizedModeTransition(any())
+ }
+
+ @Test
+ fun onDesktopWindowMinimize_pipTask_autoEnterDisabled_startMinimizeTransition() {
+ val task = setUpPipTask(autoEnterEnabled = false)
+ whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+ .thenReturn(Binder())
+
+ controller.minimizeTask(task)
+
+ verify(freeformTaskTransitionStarter).startMinimizedModeTransition(any())
+ verify(freeformTaskTransitionStarter, never()).startPipTransition(any())
+ }
+
+ @Test
fun onDesktopWindowMinimize_singleActiveTask_noWallpaperActivityToken_doesntRemoveWallpaper() {
val task = setUpFreeformTask(active = true)
val transition = Binder()
@@ -2260,7 +2289,7 @@
val task =
setUpFullscreenTask().apply {
- isTopActivityTransparent = true
+ isActivityStackTransparent = true
isTopActivityNoDisplay = true
numActivities = 1
}
@@ -2278,7 +2307,7 @@
val task =
setUpFreeformTask().apply {
- isTopActivityTransparent = true
+ isActivityStackTransparent = true
isTopActivityNoDisplay = false
numActivities = 1
}
@@ -3033,20 +3062,21 @@
.thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR)
// Drag move the task to the top edge
+ val currentDragBounds = Rect(100, 50, 500, 1000)
spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
spyController.onDragPositioningEnd(
task,
mockSurface,
Point(100, 50), /* position */
PointF(200f, 300f), /* inputCoordinate */
- Rect(100, 50, 500, 1000), /* currentDragBounds */
+ currentDragBounds,
Rect(0, 50, 2000, 2000) /* validDragArea */,
Rect() /* dragStartBounds */,
motionEvent,
desktopWindowDecoration)
// Assert bounds set to stable bounds
- val wct = getLatestToggleResizeDesktopTaskWct()
+ val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)
assertThat(findBoundsChange(wct, task)).isEqualTo(STABLE_BOUNDS)
// Assert event is properly logged
verify(desktopModeEventLogger, times(1)).logTaskResizingStarted(
@@ -3305,44 +3335,41 @@
setUpLandscapeDisplay()
val task = setUpFreeformTask()
val taskToRequest = setUpFreeformTask()
- val wctCaptor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
runOpenInstance(task, taskToRequest.taskId)
- verify(transitions).startTransition(anyInt(), wctCaptor.capture(), anyOrNull())
- assertThat(ActivityOptions.fromBundle(wctCaptor.value.hierarchyOps[0].launchOptions)
- .launchWindowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
+ verify(desktopMixedTransitionHandler).startLaunchTransition(anyInt(), any(), anyInt(),
+ anyOrNull(), anyOrNull())
+ val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_TO_FRONT)
+ assertThat(wct.hierarchyOps).hasSize(1)
+ wct.assertReorderAt(index = 0, taskToRequest)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
fun openInstance_fromFreeform_minimizesIfNeeded() {
setUpLandscapeDisplay()
- val homeTask = setUpHomeTask()
val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() }
val oldestTask = freeformTasks.first()
val newestTask = freeformTasks.last()
+ val transition = Binder()
+ val wctCaptor = argumentCaptor<WindowContainerTransaction>()
+ whenever(desktopMixedTransitionHandler.startLaunchTransition(anyInt(), wctCaptor.capture(),
+ anyInt(), anyOrNull(), anyOrNull()
+ ))
+ .thenReturn(transition)
+
runOpenInstance(newestTask, freeformTasks[1].taskId)
- val wct = getLatestWct(type = TRANSIT_OPEN)
- // Home is moved to front of everything.
- assertThat(
- wct.hierarchyOps.any { hop ->
- hop.container == homeTask.token.asBinder() && hop.toTop
- }
- ).isTrue()
- // And the oldest task isn't moved in front of home, effectively minimizing it.
- assertThat(
- wct.hierarchyOps.none { hop ->
- hop.container == oldestTask.token.asBinder() && hop.toTop
- }
- ).isTrue()
+ val wct = wctCaptor.firstValue
+ assertThat(wct.hierarchyOps.size).isEqualTo(2) // move-to-front + minimize
+ wct.assertReorderAt(0, freeformTasks[1], toTop = true)
+ wct.assertReorderAt(1, oldestTask, toTop = false)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
fun openInstance_fromFreeform_exitsImmersiveIfNeeded() {
setUpLandscapeDisplay()
- val homeTask = setUpHomeTask()
val freeformTask = setUpFreeformTask()
val immersiveTask = setUpFreeformTask()
taskRepository.setTaskInFullImmersiveState(
@@ -3352,11 +3379,13 @@
)
val runOnStartTransit = RunOnStartTransitionCallback()
val transition = Binder()
- whenever(transitions.startTransition(eq(TRANSIT_OPEN), any(), anyOrNull()))
+ whenever(desktopMixedTransitionHandler.startLaunchTransition(anyInt(), any(), anyInt(),
+ anyOrNull(), anyOrNull()
+ ))
.thenReturn(transition)
whenever(mMockDesktopImmersiveController
.exitImmersiveIfApplicable(
- any(), eq(immersiveTask.displayId), eq(freeformTask.taskId), any()))
+ any(), eq(DEFAULT_DISPLAY), eq(freeformTask.taskId), any()))
.thenReturn(
ExitResult.Exit(
exitingTask = immersiveTask.taskId,
@@ -4229,6 +4258,14 @@
return task
}
+ private fun setUpPipTask(autoEnterEnabled: Boolean): RunningTaskInfo {
+ return setUpFreeformTask().apply {
+ pictureInPictureParams = PictureInPictureParams.Builder()
+ .setAutoEnterEnabled(autoEnterEnabled)
+ .build()
+ }
+ }
+
private fun setUpHomeTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
val task = createHomeTask(displayId)
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
index 866d1b3..aee8821 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
@@ -73,10 +73,10 @@
.setLastActiveTime(100)
.build()
- /** Create a new System Modal task, i.e. a task with a single transparent activity. */
+ /** Create a new System Modal task, i.e. a task with only transparent activities. */
fun createSystemModalTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo =
createFullscreenTaskBuilder(displayId)
- .setTopActivityTransparent(true)
+ .setActivityStackTransparent(true)
.setNumActivities(1)
.build()
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
index b9d7bbf..c33005e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
@@ -43,6 +43,10 @@
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
+/**
+ * Tests for {@link SystemModalsTransitionHandler}
+ * Usage: atest WMShellUnitTests:SystemModalsTransitionHandlerTest
+ */
@SmallTest
@RunWith(AndroidTestingRunner::class)
class SystemModalsTransitionHandlerTest : ShellTestCase() {
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 c6835b7..22b45e8 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
@@ -22,7 +22,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.launcher3.Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL;
+import static com.android.launcher3.Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK;
import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
@@ -250,7 +250,7 @@
t3.taskId, -1);
}
- @EnableFlags(FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
+ @EnableFlags(FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
@Test
public void testGetRecentTasks_removesDesktopWallpaperActivity() {
RecentTaskInfo t1 = makeTaskInfo(1);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
index a15b611..8b4cf6d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
@@ -125,6 +125,8 @@
times(1)
).setAppHandleEducationTooltipCallbacks(openHandleMenuCallbackCaptor.capture(), any())
openHandleMenuCallbackCaptor.lastValue.invoke(task.taskId)
+ bgExecutor.flushAll()
+ testShellExecutor.flushAll()
verify(decor, times(1)).createHandleMenu(anyBoolean())
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index a4e3af4..88f62d1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -278,7 +278,7 @@
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
fun testDecorationIsNotCreatedForTopTranslucentActivities() {
val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN).apply {
- isTopActivityTransparent = true
+ isActivityStackTransparent = true
isTopActivityNoDisplay = false
numActivities = 1
}
@@ -780,6 +780,8 @@
times(1)
).setAppHandleEducationTooltipCallbacks(openHandleMenuCallbackCaptor.capture(), any())
openHandleMenuCallbackCaptor.lastValue.invoke(task.taskId)
+ bgExecutor.flushAll()
+ testShellExecutor.flushAll()
verify(decor, times(1)).createHandleMenu(anyBoolean())
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
index afd4607..6be234e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
@@ -74,6 +74,8 @@
import com.android.wm.shell.util.StubTransaction
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder
import org.junit.After
import org.junit.Rule
@@ -131,6 +133,8 @@
protected val mockAssistContentRequester = mock<AssistContentRequester>()
protected val bgExecutor = TestShellExecutor()
protected val mockMultiInstanceHelper = mock<MultiInstanceHelper>()
+ private val mockWindowDecorViewHostSupplier =
+ mock<WindowDecorViewHostSupplier<WindowDecorViewHost>>()
protected val mockTasksLimiter = mock<DesktopTasksLimiter>()
protected val mockFreeformTaskTransitionStarter = mock<FreeformTaskTransitionStarter>()
protected val mockActivityOrientationChangeHandler =
@@ -193,6 +197,7 @@
mockDesktopImmersiveController,
mockGenericLinksParser,
mockAssistContentRequester,
+ mockWindowDecorViewHostSupplier,
mockMultiInstanceHelper,
mockDesktopModeWindowDecorFactory,
mockInputMonitorFactory,
@@ -281,6 +286,7 @@
} else {
statusBars()
}
+ userId = context.userId
}
}
@@ -289,7 +295,7 @@
whenever(
mockDesktopModeWindowDecorFactory.create(
any(), any(), any(), any(), any(), any(), eq(task), any(), any(), any(), any(),
- any(), any(), any(), any(), any(), any(), any(), any())
+ any(), any(), any(), any(), any(), any(), any(), any(), any())
).thenReturn(decoration)
decoration.mTaskInfo = task
whenever(decoration.user).thenReturn(mockUserHandle)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 0bef4191..5d5d1f2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -39,7 +39,6 @@
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
@@ -51,7 +50,6 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static org.mockito.kotlin.VerificationKt.times;
import android.app.ActivityManager;
import android.app.assist.AssistContent;
@@ -115,6 +113,8 @@
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
import kotlin.Unit;
@@ -187,6 +187,10 @@
@Mock
private AttachedSurfaceControl mMockRootSurfaceControl;
@Mock
+ private WindowDecorViewHostSupplier<WindowDecorViewHost> mMockWindowDecorViewHostSupplier;
+ @Mock
+ private WindowDecorViewHost mMockWindowDecorViewHost;
+ @Mock
private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory;
@Mock
private TypedArray mMockRoundedCornersRadiusArray;
@@ -276,6 +280,9 @@
any())).thenReturn(mMockAppHeaderViewHolder);
when(mMockDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository);
when(mMockDesktopUserRepositories.getProfile(anyInt())).thenReturn(mDesktopRepository);
+ when(mMockWindowDecorViewHostSupplier.acquire(any(), eq(defaultDisplay)))
+ .thenReturn(mMockWindowDecorViewHost);
+ when(mMockWindowDecorViewHost.getSurfaceControl()).thenReturn(mock(SurfaceControl.class));
}
@After
@@ -1052,6 +1059,56 @@
}
@Test
+ public void updateRelayoutParams_handle_requestsAsyncViewHostRendering() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ // Make the task fullscreen so that its decoration is an App Handle.
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ final RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ mMockSplitScreenController,
+ /* applyStartTransactionOnDraw= */ true,
+ /* shouldSetTaskPositionAndCrop= */ false,
+ /* isStatusBarVisible= */ true,
+ /* isKeyguardVisibleAndOccluded= */ false,
+ /* inFullImmersiveMode= */ false,
+ new InsetsState(),
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
+
+ // App Handles don't need to be rendered in sync with the task animation, per UX.
+ assertThat(relayoutParams.mAsyncViewHost).isTrue();
+ }
+
+ @Test
+ public void updateRelayoutParams_header_requestsSyncViewHostRendering() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ // Make the task freeform so that its decoration is an App Header.
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ final RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ mMockSplitScreenController,
+ /* applyStartTransactionOnDraw= */ true,
+ /* shouldSetTaskPositionAndCrop= */ false,
+ /* isStatusBarVisible= */ true,
+ /* isKeyguardVisibleAndOccluded= */ false,
+ /* inFullImmersiveMode= */ false,
+ new InsetsState(),
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
+
+ // App Headers must be rendered in sync with the task animation, so it cannot be delayed.
+ assertThat(relayoutParams.mAsyncViewHost).isFalse();
+ }
+
+ @Test
public void relayout_fullscreenTask_appliesTransactionImmediately() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -1079,77 +1136,6 @@
}
@Test
- public void relayout_fullscreenTask_doesNotCreateViewHostImmediately() {
- final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
-
- verify(mMockSurfaceControlViewHostFactory, never()).create(any(), any(), any());
- }
-
- @Test
- public void relayout_fullscreenTask_postsViewHostCreation() {
- final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-
- ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
-
- // Once for view host, the other for the AppHandle input layer.
- verify(mMockHandler, times(2)).post(runnableArgument.capture());
- runnableArgument.getValue().run();
- verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any());
- }
-
- @Test
- @Ignore("TODO(b/367235906): Due to MONITOR_INPUT permission error")
- public void relayout_freeformTask_createsViewHostImmediately() {
- final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
- // Make non-resizable to avoid dealing with input-permissions (MONITOR_INPUT)
- taskInfo.isResizeable = false;
-
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
-
- verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any());
- verify(mMockHandler, never()).post(any());
- }
-
- @Test
- public void relayout_removesExistingHandlerCallback() {
- final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
- // Once for view host, the other for the AppHandle input layer.
- verify(mMockHandler, times(2)).post(runnableArgument.capture());
-
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
-
- verify(mMockHandler).removeCallbacks(runnableArgument.getValue());
- }
-
- @Test
- public void close_removesExistingHandlerCallback() {
- final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
- // Once for view host, the other for the AppHandle input layer.
- verify(mMockHandler, times(2)).post(runnableArgument.capture());
-
- spyWindowDecor.close();
-
- verify(mMockHandler).removeCallbacks(runnableArgument.getValue());
- }
-
- @Test
public void createMaximizeMenu_showsMenu() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final MaximizeMenu menu = mock(MaximizeMenu.class);
@@ -1349,68 +1335,41 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
- public void capturedLink_postsOnCapturedLinkExpiredRunnable() {
- final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
- final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
- null /* generic link */);
- final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
-
- // Run runnable to set captured link to expired
- verify(mMockHandler).postDelayed(runnableArgument.capture(), anyLong());
- runnableArgument.getValue().run();
-
- // Verify captured link is no longer valid by verifying link is not set as handle menu
- // browser link.
- createHandleMenu(decor);
- verifyHandleMenuCreated(null /* uri */);
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
public void capturedLink_capturedLinkNotResetToSameLink() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
null /* generic link */);
- final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
+ final ArgumentCaptor<Function1<Intent, Unit>> openInBrowserCaptor =
+ ArgumentCaptor.forClass(Function1.class);
- // Run runnable to set captured link to expired
- verify(mMockHandler).postDelayed(runnableArgument.capture(), anyLong());
- runnableArgument.getValue().run();
+ createHandleMenu(decor);
+ verify(mMockHandleMenu).show(any(),
+ any(),
+ any(),
+ any(),
+ any(),
+ any(),
+ openInBrowserCaptor.capture(),
+ any(),
+ any(),
+ any(),
+ anyBoolean()
+ );
+ // Run runnable to set captured link to used
+ openInBrowserCaptor.getValue().invoke(new Intent(Intent.ACTION_MAIN, TEST_URI1));
// Relayout decor with same captured link
decor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
- // Verify handle menu's browser link not set to captured link since link is expired
+ // Verify handle menu's browser link not set to captured link since link is already used
createHandleMenu(decor);
verifyHandleMenuCreated(null /* uri */);
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
- public void capturedLink_capturedLinkStillUsedIfExpiredAfterHandleMenuCreation() {
- final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
- final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
- null /* generic link */);
- final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
-
- // Create handle menu before link expires
- createHandleMenu(decor);
-
- // Run runnable to set captured link to expired
- verify(mMockHandler).postDelayed(runnableArgument.capture(), anyLong());
- runnableArgument.getValue().run();
-
- // Verify handle menu's browser link is set to captured link since menu was opened before
- // captured link expired
- verifyHandleMenuCreated(TEST_URI1);
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
- public void capturedLink_capturedLinkExpiresAfterClick() {
+ public void capturedLink_capturedLinkSetToUsedAfterClick() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
@@ -1772,7 +1731,7 @@
mMockGenericLinksParser, mMockAssistContentRequester, SurfaceControl.Builder::new,
mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new,
new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory,
- maximizeMenuFactory, mMockHandleMenuFactory,
+ mMockWindowDecorViewHostSupplier, maximizeMenuFactory, mMockHandleMenuFactory,
mMockMultiInstanceHelper, mMockCaptionHandleRepository, mDesktopModeEventLogger);
windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener,
mMockTouchEventListener, mMockTouchEventListener);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
index 6babf81..3bcbcbdd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
@@ -257,7 +257,7 @@
if (splitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
(SPLIT_LEFT_BOUNDS.width() / 2) - (HANDLE_WIDTH / 2)
} else {
- (SPLIT_RIGHT_BOUNDS.width() / 2) - (HANDLE_WIDTH / 2)
+ SPLIT_LEFT_BOUNDS.width() + (SPLIT_RIGHT_BOUNDS.width() / 2) - (HANDLE_WIDTH / 2)
}
}
else -> error("Invalid windowing mode")
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 04b2be0..d969346 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,6 +61,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
+import android.os.Handler;
import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.util.DisplayMetrics;
@@ -88,6 +89,8 @@
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.tests.R;
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
import org.junit.Before;
import org.junit.Rule;
@@ -130,6 +133,10 @@
@Mock
private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory;
@Mock
+ private WindowDecorViewHostSupplier<WindowDecorViewHost> mMockWindowDecorViewHostSupplier;
+ @Mock
+ private WindowDecorViewHost mMockWindowDecorViewHost;
+ @Mock
private SurfaceControlViewHost mMockSurfaceControlViewHost;
@Mock
private AttachedSurfaceControl mMockRootSurfaceControl;
@@ -143,6 +150,8 @@
private SurfaceControl mMockTaskSurface;
@Mock
private DesktopModeEventLogger mDesktopModeEventLogger;
+ @Mock
+ private Handler mMockHandler;
private final List<SurfaceControl.Transaction> mMockSurfaceControlTransactions =
new ArrayList<>();
@@ -177,6 +186,10 @@
// Add status bar inset so that WindowDecoration does not think task is in immersive mode
mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, statusBars()).setVisible(true);
doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt());
+
+ when(mMockWindowDecorViewHostSupplier.acquire(any(), any()))
+ .thenReturn(mMockWindowDecorViewHost);
+ when(mMockWindowDecorViewHost.getSurfaceControl()).thenReturn(mock(SurfaceControl.class));
}
@Test
@@ -233,10 +246,6 @@
final SurfaceControl.Builder decorContainerSurfaceBuilder =
createMockSurfaceControlBuilder(decorContainerSurface);
mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
- final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
- final SurfaceControl.Builder captionContainerSurfaceBuilder =
- createMockSurfaceControlBuilder(captionContainerSurface);
- mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
.setDisplayId(Display.DEFAULT_DISPLAY)
@@ -257,18 +266,19 @@
verify(mMockSurfaceControlStartT).setTrustedOverlay(decorContainerSurface, true);
verify(mMockSurfaceControlStartT).setWindowCrop(decorContainerSurface, 300, 100);
- verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface);
- verify(captionContainerSurfaceBuilder).setContainerLayer();
+ final SurfaceControl captionContainerSurface = mMockWindowDecorViewHost.getSurfaceControl();
+ verify(mMockSurfaceControlStartT).reparent(captionContainerSurface, decorContainerSurface);
verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64);
verify(mMockSurfaceControlStartT).show(captionContainerSurface);
- verify(mMockSurfaceControlViewHostFactory).create(any(), eq(defaultDisplay), any());
-
- verify(mMockSurfaceControlViewHost)
- .setView(same(mMockView),
- argThat(lp -> lp.height == 64
- && lp.width == 300
- && (lp.flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0));
+ verify(mMockWindowDecorViewHost).updateView(
+ same(mMockView),
+ argThat(lp -> lp.height == 64
+ && lp.width == 300
+ && (lp.flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0),
+ eq(taskInfo.configuration),
+ any(),
+ eq(null) /* onDrawTransaction */);
verify(mMockView).setTaskFocusState(true);
verify(mMockWindowContainerTransaction).addInsetsSource(
eq(taskInfo.token),
@@ -297,10 +307,6 @@
final SurfaceControl.Builder decorContainerSurfaceBuilder =
createMockSurfaceControlBuilder(decorContainerSurface);
mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
- final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
- final SurfaceControl.Builder captionContainerSurfaceBuilder =
- createMockSurfaceControlBuilder(captionContainerSurface);
- mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
mMockSurfaceControlTransactions.add(t);
@@ -323,7 +329,7 @@
windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
- verify(mMockSurfaceControlViewHost, never()).release();
+ verify(mMockWindowDecorViewHost, never()).release(any());
verify(t, never()).apply();
verify(mMockWindowContainerTransaction, never())
.removeInsetsSource(eq(taskInfo.token), any(), anyInt(), anyInt());
@@ -333,9 +339,8 @@
taskInfo.isVisible = false;
windowDecor.relayout(taskInfo, false /* hasGlobalFocus */);
- final InOrder releaseOrder = inOrder(t2, mMockSurfaceControlViewHost);
- releaseOrder.verify(mMockSurfaceControlViewHost).release();
- releaseOrder.verify(t2).remove(captionContainerSurface);
+ final InOrder releaseOrder = inOrder(t2, mMockWindowDecorViewHostSupplier);
+ releaseOrder.verify(mMockWindowDecorViewHostSupplier).release(mMockWindowDecorViewHost, t2);
releaseOrder.verify(t2).remove(decorContainerSurface);
releaseOrder.verify(t2).apply();
// Expect to remove two insets sources, the caption insets and the mandatory gesture insets.
@@ -383,8 +388,8 @@
verify(mMockDisplayController).removeDisplayWindowListener(same(listener));
assertThat(mRelayoutResult.mRootView).isSameInstanceAs(mMockView);
- verify(mMockSurfaceControlViewHostFactory).create(any(), eq(mockDisplay), any());
- verify(mMockSurfaceControlViewHost).setView(same(mMockView), any());
+ verify(mMockWindowDecorViewHostSupplier).acquire(any(), eq(mockDisplay));
+ verify(mMockWindowDecorViewHost).updateView(same(mMockView), any(), any(), any(), any());
}
@Test
@@ -397,10 +402,6 @@
final SurfaceControl.Builder decorContainerSurfaceBuilder =
createMockSurfaceControlBuilder(decorContainerSurface);
mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
- final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
- final SurfaceControl.Builder captionContainerSurfaceBuilder =
- createMockSurfaceControlBuilder(captionContainerSurface);
- mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
mMockSurfaceControlTransactions.add(t);
@@ -436,8 +437,7 @@
windowDecor.mDecorWindowContext.getResources(), mRelayoutParams.mCaptionHeightId);
verify(mMockSurfaceControlAddWindowT).setWindowCrop(additionalWindowSurface, width, height);
verify(mMockSurfaceControlAddWindowT).show(additionalWindowSurface);
- verify(mMockSurfaceControlViewHostFactory, Mockito.times(2))
- .create(any(), eq(defaultDisplay), any());
+ verify(mMockSurfaceControlViewHostFactory).create(any(), eq(defaultDisplay), any());
}
@Test
@@ -450,10 +450,6 @@
final SurfaceControl.Builder decorContainerSurfaceBuilder =
createMockSurfaceControlBuilder(decorContainerSurface);
mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
- final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
- final SurfaceControl.Builder captionContainerSurfaceBuilder =
- createMockSurfaceControlBuilder(captionContainerSurface);
- mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
mMockSurfaceControlTransactions.add(t);
@@ -472,8 +468,8 @@
windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
- verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface);
- verify(captionContainerSurfaceBuilder).setContainerLayer();
+ final SurfaceControl captionContainerSurface = mMockWindowDecorViewHost.getSurfaceControl();
+ verify(mMockSurfaceControlStartT).reparent(captionContainerSurface, decorContainerSurface);
// Width of the captionContainerSurface should match the width of TASK_BOUNDS
verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64);
verify(mMockSurfaceControlStartT).show(captionContainerSurface);
@@ -489,10 +485,6 @@
final SurfaceControl.Builder decorContainerSurfaceBuilder =
createMockSurfaceControlBuilder(decorContainerSurface);
mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
- final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
- final SurfaceControl.Builder captionContainerSurfaceBuilder =
- createMockSurfaceControlBuilder(captionContainerSurface);
- mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
mMockSurfaceControlTransactions.add(t);
@@ -509,10 +501,11 @@
taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
- windowDecor.relayout(taskInfo, true /* applyStartTransactionOnDraw */,
- true /* hasGlobalFocus */, Region.obtain());
+ mRelayoutParams.mApplyStartTransactionOnDraw = true;
+ windowDecor.relayout(taskInfo, true /* hasGlobalFocus */, Region.obtain());
- verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT);
+ verify(mMockWindowDecorViewHost).updateView(any(), any(), any(), any(),
+ eq(mMockSurfaceControlStartT));
}
@Test
@@ -901,37 +894,69 @@
}
@Test
- public void updateViewHost_applyTransactionOnDrawIsTrue_surfaceControlIsUpdated() {
+ public void relayout_applyTransactionOnDrawIsTrue_updatesViewWithDrawTransaction() {
final TestWindowDecoration windowDecor = createWindowDecoration(
- new TestRunningTaskInfoBuilder().build());
+ new TestRunningTaskInfoBuilder()
+ .setVisible(true)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM)
+ .build());
mRelayoutParams.mApplyStartTransactionOnDraw = true;
mRelayoutResult.mRootView = mMockView;
- windowDecor.updateViewHost(mRelayoutParams, mMockSurfaceControlStartT, mRelayoutResult);
+ windowDecor.relayout(
+ windowDecor.mTaskInfo,
+ /* hasGlobalFocus= */ true,
+ Region.obtain());
- verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT);
+ verify(mMockWindowDecorViewHost)
+ .updateView(
+ eq(mRelayoutResult.mRootView),
+ any(),
+ eq(windowDecor.mTaskInfo.configuration),
+ any(),
+ eq(mMockSurfaceControlStartT));
+ windowDecor.close();
}
@Test
- public void updateViewHost_nullDrawTransaction_applyTransactionOnDrawIsTrue_throwsException() {
+ public void relayout_applyTransactionOnDrawIsTrue_asyncViewHostRendering_throwsException() {
final TestWindowDecoration windowDecor = createWindowDecoration(
- new TestRunningTaskInfoBuilder().build());
+ new TestRunningTaskInfoBuilder()
+ .setVisible(true)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .build());
mRelayoutParams.mApplyStartTransactionOnDraw = true;
+ mRelayoutParams.mAsyncViewHost = true;
mRelayoutResult.mRootView = mMockView;
assertThrows(IllegalArgumentException.class,
- () -> windowDecor.updateViewHost(
- mRelayoutParams, null /* onDrawTransaction */, mRelayoutResult));
+ () -> windowDecor.relayout(
+ windowDecor.mTaskInfo,
+ /* hasGlobalFocus= */ true,
+ Region.obtain()));
+ windowDecor.close();
}
@Test
- public void updateViewHost_nullDrawTransaction_applyTransactionOnDrawIsFalse_doesNotThrow() {
+ public void relayout_asyncViewHostRendering() {
final TestWindowDecoration windowDecor = createWindowDecoration(
- new TestRunningTaskInfoBuilder().build());
+ new TestRunningTaskInfoBuilder()
+ .setVisible(true)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .build());
mRelayoutParams.mApplyStartTransactionOnDraw = false;
+ mRelayoutParams.mAsyncViewHost = true;
mRelayoutResult.mRootView = mMockView;
- windowDecor.updateViewHost(mRelayoutParams, null /* onDrawTransaction */, mRelayoutResult);
+ windowDecor.relayout(
+ windowDecor.mTaskInfo,
+ /* hasGlobalFocus= */ true,
+ Region.obtain());
+
+ verify(mMockWindowDecorViewHost)
+ .updateViewAsync(eq(mRelayoutResult.mRootView), any(),
+ eq(windowDecor.mTaskInfo.configuration), any());
+ windowDecor.close();
}
@Test
@@ -1015,7 +1040,8 @@
new MockObjectSupplier<>(mMockSurfaceControlTransactions,
() -> mock(SurfaceControl.Transaction.class)),
() -> mMockWindowContainerTransaction, () -> mMockTaskSurface,
- mMockSurfaceControlViewHostFactory, mDesktopModeEventLogger);
+ mMockSurfaceControlViewHostFactory, mMockWindowDecorViewHostSupplier,
+ mDesktopModeEventLogger);
}
private class MockObjectSupplier<T> implements Supplier<T> {
@@ -1049,18 +1075,22 @@
private class TestWindowDecoration extends WindowDecoration<TestView> {
TestWindowDecoration(Context context, @NonNull Context userContext,
DisplayController displayController,
- ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo,
+ ShellTaskOrganizer taskOrganizer,
+ ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
Supplier<SurfaceControl> surfaceControlSupplier,
SurfaceControlViewHostFactory surfaceControlViewHostFactory,
+ @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost>
+ windowDecorViewHostSupplier,
DesktopModeEventLogger desktopModeEventLogger) {
- super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
- surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
+ super(context, userContext, displayController, taskOrganizer, taskInfo,
+ taskSurface, surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
windowContainerTransactionSupplier, surfaceControlSupplier,
- surfaceControlViewHostFactory, desktopModeEventLogger);
+ surfaceControlViewHostFactory, windowDecorViewHostSupplier,
+ desktopModeEventLogger);
}
void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus) {
@@ -1071,8 +1101,12 @@
@Override
void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus,
@NonNull Region displayExclusionRegion) {
- relayout(taskInfo, false /* applyStartTransactionOnDraw */, hasGlobalFocus,
- displayExclusionRegion);
+ mRelayoutParams.mRunningTaskInfo = taskInfo;
+ mRelayoutParams.mHasGlobalFocus = hasGlobalFocus;
+ mRelayoutParams.mDisplayExclusionRegion.set(displayExclusionRegion);
+ mRelayoutParams.mLayoutResId = R.layout.caption_layout;
+ relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT,
+ mMockWindowContainerTransaction, mMockView, mRelayoutResult);
}
@Override
@@ -1096,13 +1130,8 @@
void relayout(ActivityManager.RunningTaskInfo taskInfo,
boolean applyStartTransactionOnDraw, boolean hasGlobalFocus,
@NonNull Region displayExclusionRegion) {
- mRelayoutParams.mRunningTaskInfo = taskInfo;
mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
- mRelayoutParams.mLayoutResId = R.layout.caption_layout;
- mRelayoutParams.mHasGlobalFocus = hasGlobalFocus;
- mRelayoutParams.mDisplayExclusionRegion.set(displayExclusionRegion);
- relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT,
- mMockWindowContainerTransaction, mMockView, mRelayoutResult);
+ relayout(taskInfo, hasGlobalFocus, displayExclusionRegion);
}
private AdditionalViewContainer addTestViewContainer() {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/DesktopMenuPositionUtilityTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/DesktopMenuPositionUtilityTest.kt
new file mode 100644
index 0000000..7b42ff4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/DesktopMenuPositionUtilityTest.kt
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.common
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
+import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
+import com.android.wm.shell.splitscreen.SplitScreenController
+import org.junit.runner.RunWith
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+/**
+ * Tests for [DesktopMenuPositionUtility].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:DesktopMenuPositionUtilityTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopMenuPositionUtilityTest : ShellTestCase() {
+
+ @Mock private val mockSplitScreenController = mock<SplitScreenController>()
+
+ @Test
+ fun testFullscreenPositionCalculation() {
+ val task = setupTaskInfo(WINDOWING_MODE_FULLSCREEN)
+ val result =
+ calculateMenuPosition(
+ splitScreenController = mockSplitScreenController,
+ taskInfo = task,
+ MARGIN_START,
+ MARGIN_TOP,
+ CAPTION_X,
+ CAPTION_Y,
+ CAPTION_WIDTH,
+ MENU_WIDTH,
+ isRtl = false,
+ )
+ assertEquals(CAPTION_X + (CAPTION_WIDTH / 2) - (MENU_WIDTH / 2), result.x)
+ assertEquals(CAPTION_Y + MARGIN_TOP, result.y)
+ }
+
+ @Test
+ fun testSplitLeftPositionCalculation() {
+ val task = setupTaskInfo(WINDOWING_MODE_MULTI_WINDOW)
+ setupMockSplitScreenController(
+ splitPosition = SPLIT_POSITION_TOP_OR_LEFT,
+ isLeftRightSplit = true,
+ )
+ val result =
+ calculateMenuPosition(
+ splitScreenController = mockSplitScreenController,
+ taskInfo = task,
+ MARGIN_START,
+ MARGIN_TOP,
+ CAPTION_X,
+ CAPTION_Y,
+ CAPTION_WIDTH,
+ MENU_WIDTH,
+ isRtl = false,
+ )
+ assertEquals(CAPTION_X + (CAPTION_WIDTH / 2) - (MENU_WIDTH / 2), result.x)
+ assertEquals(CAPTION_Y + MARGIN_TOP, result.y)
+ }
+
+ @Test
+ fun testSplitRightPositionCalculation() {
+ val task = setupTaskInfo(WINDOWING_MODE_MULTI_WINDOW)
+ setupMockSplitScreenController(
+ splitPosition = SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ isLeftRightSplit = true,
+ )
+ val result =
+ calculateMenuPosition(
+ splitScreenController = mockSplitScreenController,
+ taskInfo = task,
+ MARGIN_START,
+ MARGIN_TOP,
+ CAPTION_X,
+ CAPTION_Y,
+ CAPTION_WIDTH,
+ MENU_WIDTH,
+ isRtl = false,
+ )
+ assertEquals(
+ CAPTION_X + (CAPTION_WIDTH / 2) - (MENU_WIDTH / 2) + SPLIT_LEFT_BOUNDS.width(),
+ result.x,
+ )
+ assertEquals(CAPTION_Y + MARGIN_TOP, result.y)
+ }
+
+ @Test
+ fun testSplitTopPositionCalculation() {
+ val task = setupTaskInfo(WINDOWING_MODE_MULTI_WINDOW)
+ setupMockSplitScreenController(
+ splitPosition = SPLIT_POSITION_TOP_OR_LEFT,
+ isLeftRightSplit = false,
+ )
+ val result =
+ calculateMenuPosition(
+ splitScreenController = mockSplitScreenController,
+ taskInfo = task,
+ MARGIN_START,
+ MARGIN_TOP,
+ CAPTION_X,
+ CAPTION_Y,
+ CAPTION_WIDTH,
+ MENU_WIDTH,
+ isRtl = false,
+ )
+ assertEquals(CAPTION_X + (CAPTION_WIDTH / 2) - (MENU_WIDTH / 2), result.x)
+ assertEquals(CAPTION_Y + MARGIN_TOP, result.y)
+ }
+
+ @Test
+ fun testSplitBottomPositionCalculation() {
+ val task = setupTaskInfo(WINDOWING_MODE_MULTI_WINDOW)
+ setupMockSplitScreenController(
+ splitPosition = SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ isLeftRightSplit = false,
+ )
+ val result =
+ calculateMenuPosition(
+ splitScreenController = mockSplitScreenController,
+ taskInfo = task,
+ MARGIN_START,
+ MARGIN_TOP,
+ CAPTION_X,
+ CAPTION_Y,
+ CAPTION_WIDTH,
+ MENU_WIDTH,
+ isRtl = false,
+ )
+ assertEquals(CAPTION_X + (CAPTION_WIDTH / 2) - (MENU_WIDTH / 2), result.x)
+ assertEquals(CAPTION_Y + MARGIN_TOP + SPLIT_TOP_BOUNDS.height(), result.y)
+ }
+
+ private fun setupTaskInfo(windowingMode: Int): RunningTaskInfo {
+ return TestRunningTaskInfoBuilder().setWindowingMode(windowingMode).build()
+ }
+
+ private fun setupMockSplitScreenController(isLeftRightSplit: Boolean, splitPosition: Int) {
+ whenever(mockSplitScreenController.getSplitPosition(anyInt())).thenReturn(splitPosition)
+ whenever(mockSplitScreenController.getRefStageBounds(any(), any())).thenAnswer {
+ (it.arguments.first() as Rect).set(
+ if (isLeftRightSplit) {
+ SPLIT_LEFT_BOUNDS
+ } else {
+ SPLIT_TOP_BOUNDS
+ }
+ )
+ (it.arguments[1] as Rect).set(
+ if (isLeftRightSplit) {
+ SPLIT_RIGHT_BOUNDS
+ } else {
+ SPLIT_BOTTOM_BOUNDS
+ }
+ )
+ }
+ whenever(mockSplitScreenController.isLeftRightSplit).thenReturn(isLeftRightSplit)
+ }
+
+ companion object {
+ private val SPLIT_LEFT_BOUNDS = Rect(0, 0, 1280, 1600)
+ private val SPLIT_RIGHT_BOUNDS = Rect(1280, 0, 2560, 1600)
+ private val SPLIT_TOP_BOUNDS = Rect(0, 0, 2560, 800)
+ private val SPLIT_BOTTOM_BOUNDS = Rect(0, 800, 2560, 1600)
+ private const val CAPTION_X = 800
+ private const val CAPTION_Y = 50
+ private const val MARGIN_START = 30
+ private const val MARGIN_TOP = 50
+ private const val MENU_WIDTH = 500
+ private const val CAPTION_WIDTH = 200
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostTest.kt
index 2f223de..4f19f34 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostTest.kt
@@ -18,7 +18,6 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.SurfaceControl
-import android.view.SurfaceControlViewHost
import android.view.View
import android.view.WindowManager
import androidx.test.filters.SmallTest
@@ -28,7 +27,6 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
-import org.junit.Assert.assertThrows
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
@@ -57,54 +55,8 @@
onDrawTransaction = null,
)
- assertThat(windowDecorViewHost.viewHost).isNotNull()
- assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(view)
- }
-
- @Test
- fun updateView_alreadyLaidOut_relayouts() = runTest {
- val windowDecorViewHost = createDefaultViewHost()
- val view = View(context)
- windowDecorViewHost.updateView(
- view = view,
- attrs = WindowManager.LayoutParams(100, 100),
- configuration = context.resources.configuration,
- onDrawTransaction = null,
- )
-
- val otherParams = WindowManager.LayoutParams(200, 200)
- windowDecorViewHost.updateView(
- view = view,
- attrs = otherParams,
- configuration = context.resources.configuration,
- onDrawTransaction = null,
- )
-
- assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(view)
- assertThat(windowDecorViewHost.viewHost!!.view!!.layoutParams.width)
- .isEqualTo(otherParams.width)
- }
-
- @Test
- fun updateView_replacingView_throws() = runTest {
- val windowDecorViewHost = createDefaultViewHost()
- val view = View(context)
- windowDecorViewHost.updateView(
- view = view,
- attrs = WindowManager.LayoutParams(100, 100),
- configuration = context.resources.configuration,
- onDrawTransaction = null,
- )
-
- val otherView = View(context)
- assertThrows(Exception::class.java) {
- windowDecorViewHost.updateView(
- view = otherView,
- attrs = WindowManager.LayoutParams(100, 100),
- configuration = context.resources.configuration,
- onDrawTransaction = null,
- )
- }
+ assertThat(windowDecorViewHost.viewHostAdapter.isInitialized()).isTrue()
+ assertThat(windowDecorViewHost.view()).isEqualTo(view)
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -123,7 +75,7 @@
)
// No view host yet, since the coroutine hasn't run.
- assertThat(windowDecorViewHost.viewHost).isNull()
+ assertThat(windowDecorViewHost.viewHostAdapter.isInitialized()).isFalse()
windowDecorViewHost.updateView(
view = syncView,
@@ -135,14 +87,13 @@
// Would run coroutine if it hadn't been cancelled.
advanceUntilIdle()
- assertThat(windowDecorViewHost.viewHost).isNotNull()
- assertThat(windowDecorViewHost.viewHost!!.view).isNotNull()
+ assertThat(windowDecorViewHost.viewHostAdapter.isInitialized()).isTrue()
+ assertThat(windowDecorViewHost.view()).isNotNull()
// View host view/attrs should match the ones from the sync call, plus, since the
// sync/async were made with different views, if the job hadn't been cancelled there
// would've been an exception thrown as replacing views isn't allowed.
- assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(syncView)
- assertThat(windowDecorViewHost.viewHost!!.view!!.layoutParams.width)
- .isEqualTo(syncAttrs.width)
+ assertThat(windowDecorViewHost.view()).isEqualTo(syncView)
+ assertThat(windowDecorViewHost.view()!!.layoutParams.width).isEqualTo(syncAttrs.width)
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -158,11 +109,11 @@
configuration = context.resources.configuration,
)
- assertThat(windowDecorViewHost.viewHost).isNull()
+ assertThat(windowDecorViewHost.viewHostAdapter.isInitialized()).isFalse()
advanceUntilIdle()
- assertThat(windowDecorViewHost.viewHost).isNotNull()
+ assertThat(windowDecorViewHost.viewHostAdapter.isInitialized()).isTrue()
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -185,9 +136,8 @@
advanceUntilIdle()
- assertThat(windowDecorViewHost.viewHost).isNotNull()
- assertThat(windowDecorViewHost.viewHost!!.view).isNotNull()
- assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(otherView)
+ assertThat(windowDecorViewHost.viewHostAdapter.isInitialized()).isTrue()
+ assertThat(windowDecorViewHost.view()).isEqualTo(otherView)
}
@Test
@@ -205,8 +155,7 @@
val t = mock(SurfaceControl.Transaction::class.java)
windowDecorViewHost.release(t)
- verify(windowDecorViewHost.viewHost!!).release()
- verify(t).remove(windowDecorViewHost.surfaceControl)
+ verify(windowDecorViewHost.viewHostAdapter).release(t)
}
private fun CoroutineScope.createDefaultViewHost() =
@@ -214,8 +163,8 @@
context = context,
mainScope = this,
display = context.display,
- surfaceControlViewHostFactory = { c, d, wwm, s ->
- spy(SurfaceControlViewHost(c, d, wwm, s))
- },
+ viewHostAdapter = spy(SurfaceControlViewHostAdapter(context, context.display)),
)
+
+ private fun DefaultWindowDecorViewHost.view(): View? = viewHostAdapter.viewHost?.view
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/PooledWindowDecorViewHostSupplierTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/PooledWindowDecorViewHostSupplierTest.kt
new file mode 100644
index 0000000..92f5def
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/PooledWindowDecorViewHostSupplierTest.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.common.viewhost
+
+import android.content.res.Configuration
+import android.graphics.Region
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.SurfaceControl
+import android.view.View
+import android.view.WindowManager
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.util.StubTransaction
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.mock
+
+/**
+ * Tests for [PooledWindowDecorViewHostSupplier].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:PooledWindowDecorViewHostSupplierTest
+ */
+@SmallTest
+@RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class PooledWindowDecorViewHostSupplierTest : ShellTestCase() {
+
+ private val testExecutor = TestShellExecutor()
+ private val testShellInit = ShellInit(testExecutor)
+
+ private lateinit var supplier: PooledWindowDecorViewHostSupplier
+
+ @Test
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun onInit_warmsAndPoolsViewHosts() = runTest {
+ supplier = createSupplier(maxPoolSize = 5, preWarmSize = 2)
+
+ testExecutor.flushAll()
+ advanceUntilIdle()
+
+ val viewHost1 = supplier.acquire(context, context.display) as ReusableWindowDecorViewHost
+ val viewHost2 = supplier.acquire(context, context.display) as ReusableWindowDecorViewHost
+
+ // Acquired warmed up view hosts from the pool.
+ assertThat(viewHost1.viewHostAdapter.isInitialized()).isTrue()
+ assertThat(viewHost2.viewHostAdapter.isInitialized()).isTrue()
+ }
+
+ @Test(expected = Throwable::class)
+ fun onInit_warmUpSizeExceedsPoolSize_throws() = runTest {
+ createSupplier(maxPoolSize = 3, preWarmSize = 4)
+ }
+
+ @Test
+ fun acquire_poolBelowLimit_caches() = runTest {
+ supplier = createSupplier(maxPoolSize = 5)
+
+ val viewHost = FakeWindowDecorViewHost()
+ supplier.release(viewHost, StubTransaction())
+
+ assertThat(supplier.acquire(context, context.display)).isEqualTo(viewHost)
+ }
+
+ @Test
+ fun release_poolBelowLimit_doesNotReleaseViewHost() = runTest {
+ supplier = createSupplier(maxPoolSize = 5)
+
+ val viewHost = FakeWindowDecorViewHost()
+ val mockT = mock<SurfaceControl.Transaction>()
+ supplier.release(viewHost, mockT)
+
+ assertThat(viewHost.released).isFalse()
+ }
+
+ @Test
+ fun release_poolAtLimit_doesNotCache() = runTest {
+ supplier = createSupplier(maxPoolSize = 1)
+ val viewHost = FakeWindowDecorViewHost()
+ supplier.release(viewHost, StubTransaction()) // Maxes pool.
+
+ val viewHost2 = FakeWindowDecorViewHost()
+ supplier.release(viewHost2, StubTransaction()) // Beyond limit.
+
+ assertThat(supplier.acquire(context, context.display)).isEqualTo(viewHost)
+ // Second one wasn't cached, so the acquired one should've been a new instance.
+ assertThat(supplier.acquire(context, context.display)).isNotEqualTo(viewHost2)
+ }
+
+ @Test
+ fun release_poolAtLimit_releasesViewHost() = runTest {
+ supplier = createSupplier(maxPoolSize = 1)
+ val viewHost = FakeWindowDecorViewHost()
+ supplier.release(viewHost, StubTransaction()) // Maxes pool.
+
+ val viewHost2 = FakeWindowDecorViewHost()
+ val mockT = mock<SurfaceControl.Transaction>()
+ supplier.release(viewHost2, mockT) // Beyond limit.
+
+ // Second one doesn't fit, so it needs to be released.
+ assertThat(viewHost2.released).isTrue()
+ }
+
+ private fun CoroutineScope.createSupplier(maxPoolSize: Int, preWarmSize: Int = 0) =
+ PooledWindowDecorViewHostSupplier(context, this, testShellInit, maxPoolSize, preWarmSize)
+ .also { testShellInit.init() }
+
+ private class FakeWindowDecorViewHost : WindowDecorViewHost {
+ var released = false
+ private set
+
+ override val surfaceControl: SurfaceControl
+ get() = SurfaceControl()
+
+ override fun updateView(
+ view: View,
+ attrs: WindowManager.LayoutParams,
+ configuration: Configuration,
+ touchableRegion: Region?,
+ onDrawTransaction: SurfaceControl.Transaction?,
+ ) {}
+
+ override fun updateViewAsync(
+ view: View,
+ attrs: WindowManager.LayoutParams,
+ configuration: Configuration,
+ touchableRegion: Region?,
+ ) {}
+
+ override fun release(t: SurfaceControl.Transaction) {
+ released = true
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHostTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHostTest.kt
new file mode 100644
index 0000000..d99a482
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHostTest.kt
@@ -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.wm.shell.windowdecor.common.viewhost
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.SurfaceControl
+import android.view.View
+import android.view.WindowManager
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+
+/**
+ * Tests for [ReusableWindowDecorViewHost].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:ReusableWindowDecorViewHostTest
+ */
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class ReusableWindowDecorViewHostTest : ShellTestCase() {
+
+ @Test
+ fun update_differentView_replacesView() = runTest {
+ val view = View(context)
+ val lp = WindowManager.LayoutParams()
+ val reusableVH = createReusableViewHost()
+ reusableVH.updateView(view, lp, context.resources.configuration, null)
+
+ assertThat(reusableVH.rootView.childCount).isEqualTo(1)
+ assertThat(reusableVH.rootView.getChildAt(0)).isEqualTo(view)
+
+ val newView = View(context)
+ val newLp = WindowManager.LayoutParams()
+ reusableVH.updateView(newView, newLp, context.resources.configuration, null)
+
+ assertThat(reusableVH.rootView.childCount).isEqualTo(1)
+ assertThat(reusableVH.rootView.getChildAt(0)).isEqualTo(newView)
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun updateView_clearsPendingAsyncJob() = runTest {
+ val reusableVH = createReusableViewHost()
+ val asyncView = View(context)
+ val syncView = View(context)
+ val asyncAttrs = WindowManager.LayoutParams(100, 100)
+ val syncAttrs = WindowManager.LayoutParams(200, 200)
+
+ reusableVH.updateViewAsync(
+ view = asyncView,
+ attrs = asyncAttrs,
+ configuration = context.resources.configuration,
+ )
+
+ // No view host yet, since the coroutine hasn't run.
+ assertThat(reusableVH.viewHostAdapter.isInitialized()).isFalse()
+
+ reusableVH.updateView(
+ view = syncView,
+ attrs = syncAttrs,
+ configuration = context.resources.configuration,
+ onDrawTransaction = null,
+ )
+
+ // Would run coroutine if it hadn't been cancelled.
+ advanceUntilIdle()
+
+ assertThat(reusableVH.viewHostAdapter.isInitialized()).isTrue()
+ // View host view/attrs should match the ones from the sync call.
+ assertThat(reusableVH.rootView.getChildAt(0)).isEqualTo(syncView)
+ assertThat(reusableVH.view()!!.layoutParams.width).isEqualTo(syncAttrs.width)
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun updateViewAsync() = runTest {
+ val reusableVH = createReusableViewHost()
+ val view = View(context)
+ val attrs = WindowManager.LayoutParams(100, 100)
+
+ reusableVH.updateViewAsync(
+ view = view,
+ attrs = attrs,
+ configuration = context.resources.configuration,
+ )
+
+ assertThat(reusableVH.viewHostAdapter.isInitialized()).isFalse()
+
+ advanceUntilIdle()
+
+ assertThat(reusableVH.viewHostAdapter.isInitialized()).isTrue()
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun updateViewAsync_clearsPendingAsyncJob() = runTest {
+ val reusableVH = createReusableViewHost()
+
+ val view = View(context)
+ reusableVH.updateViewAsync(
+ view = view,
+ attrs = WindowManager.LayoutParams(100, 100),
+ configuration = context.resources.configuration,
+ )
+ val otherView = View(context)
+ reusableVH.updateViewAsync(
+ view = otherView,
+ attrs = WindowManager.LayoutParams(100, 100),
+ configuration = context.resources.configuration,
+ )
+
+ advanceUntilIdle()
+
+ assertThat(reusableVH.viewHostAdapter.isInitialized()).isTrue()
+ assertThat(reusableVH.rootView.getChildAt(0)).isEqualTo(otherView)
+ }
+
+ @Test
+ fun release() = runTest {
+ val reusableVH = createReusableViewHost()
+
+ val view = View(context)
+ reusableVH.updateView(
+ view = view,
+ attrs = WindowManager.LayoutParams(100, 100),
+ configuration = context.resources.configuration,
+ onDrawTransaction = null,
+ )
+
+ val t = mock(SurfaceControl.Transaction::class.java)
+ reusableVH.release(t)
+
+ verify(reusableVH.viewHostAdapter).release(t)
+ }
+
+ @Test
+ fun warmUp_addsRootView() = runTest {
+ val reusableVH = createReusableViewHost().apply { warmUp() }
+
+ assertThat(reusableVH.viewHostAdapter.isInitialized()).isTrue()
+ assertThat(reusableVH.view()).isEqualTo(reusableVH.rootView)
+ }
+
+ private fun CoroutineScope.createReusableViewHost() =
+ ReusableWindowDecorViewHost(
+ context = context,
+ mainScope = this,
+ display = context.display,
+ id = 1,
+ viewHostAdapter = spy(SurfaceControlViewHostAdapter(context, context.display)),
+ )
+
+ private fun ReusableWindowDecorViewHost.view(): View? = viewHostAdapter.viewHost?.view
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/SurfaceControlViewHostAdapterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/SurfaceControlViewHostAdapterTest.kt
new file mode 100644
index 0000000..5109a7c3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/SurfaceControlViewHostAdapterTest.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.wm.shell.windowdecor.common.viewhost
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.SurfaceControl
+import android.view.SurfaceControlViewHost
+import android.view.View
+import android.view.WindowManager
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+
+/**
+ * Tests for [SurfaceControlViewHostAdapter].
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:SurfaceControlViewHostAdapterTest
+ */
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class SurfaceControlViewHostAdapterTest : ShellTestCase() {
+
+ private lateinit var adapter: SurfaceControlViewHostAdapter
+
+ @Before
+ fun setUp() {
+ adapter = SurfaceControlViewHostAdapter(
+ context,
+ context.display,
+ surfaceControlViewHostFactory = { c, d, wwm, s ->
+ spy(SurfaceControlViewHost(c, d, wwm, s))
+ }
+ )
+ }
+
+ @Test
+ fun prepareViewHost() {
+ adapter.prepareViewHost(context.resources.configuration, touchableRegion = null)
+
+ assertThat(adapter.viewHost).isNotNull()
+ }
+
+ @Test
+ fun prepareViewHost_alreadyCreated_skips() {
+ adapter.prepareViewHost(context.resources.configuration, touchableRegion = null)
+
+ val viewHost = adapter.viewHost!!
+
+ adapter.prepareViewHost(context.resources.configuration, touchableRegion = null)
+
+ assertThat(adapter.viewHost).isEqualTo(viewHost)
+ }
+
+ @Test
+ fun updateView_layoutInViewHost() {
+ val view = View(context)
+ adapter.prepareViewHost(context.resources.configuration, touchableRegion = null)
+
+ adapter.updateView(
+ view = view,
+ attrs = WindowManager.LayoutParams(100, 100)
+ )
+
+ assertThat(adapter.isInitialized()).isTrue()
+ assertThat(adapter.view()).isEqualTo(view)
+ }
+
+ @Test
+ fun updateView_alreadyLaidOut_relayouts() {
+ val view = View(context)
+ adapter.prepareViewHost(context.resources.configuration, touchableRegion = null)
+ adapter.updateView(
+ view = view,
+ attrs = WindowManager.LayoutParams(100, 100)
+ )
+
+ val otherParams = WindowManager.LayoutParams(200, 200)
+ adapter.updateView(
+ view = view,
+ attrs = otherParams
+ )
+
+ assertThat(adapter.view()).isEqualTo(view)
+ assertThat(adapter.view()!!.layoutParams.width).isEqualTo(otherParams.width)
+ }
+
+ @Test
+ fun updateView_replacingView_throws() {
+ val view = View(context)
+ adapter.prepareViewHost(context.resources.configuration, touchableRegion = null)
+ adapter.updateView(
+ view = view,
+ attrs = WindowManager.LayoutParams(100, 100)
+ )
+
+ val otherView = View(context)
+ assertThrows(Exception::class.java) {
+ adapter.updateView(
+ view = otherView,
+ attrs = WindowManager.LayoutParams(100, 100)
+ )
+ }
+ }
+
+ @Test
+ fun release() {
+ adapter.prepareViewHost(context.resources.configuration, touchableRegion = null)
+ adapter.updateView(
+ view = View(context),
+ attrs = WindowManager.LayoutParams(100, 100)
+ )
+
+ val mockT = mock(SurfaceControl.Transaction::class.java)
+ adapter.release(mockT)
+
+ verify(adapter.viewHost!!).release()
+ verify(mockT).remove(adapter.rootSurface)
+ }
+
+ private fun SurfaceControlViewHostAdapter.view(): View? = viewHost?.view
+}
\ No newline at end of file
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 49254d1..dbb8914 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -40,20 +40,21 @@
}
ApkAssetsPtr ApkAssets::Load(const std::string& path, package_property_t flags) {
- return Load(ZipAssetsProvider::Create(path, flags), flags);
+ return LoadImpl(ZipAssetsProvider::Create(path, flags), flags);
}
ApkAssetsPtr ApkAssets::LoadFromFd(base::unique_fd fd, const std::string& debug_name,
package_property_t flags, off64_t offset, off64_t len) {
- return Load(ZipAssetsProvider::Create(std::move(fd), debug_name, offset, len), flags);
+ return LoadImpl(ZipAssetsProvider::Create(std::move(fd), debug_name, offset, len), flags);
}
-ApkAssetsPtr ApkAssets::Load(std::unique_ptr<AssetsProvider> assets, package_property_t flags) {
+ApkAssetsPtr ApkAssets::LoadImpl(std::unique_ptr<AssetsProvider>&& assets,
+ package_property_t flags) {
return LoadImpl(std::move(assets), flags, nullptr /* idmap_asset */, nullptr /* loaded_idmap */);
}
-ApkAssetsPtr ApkAssets::LoadTable(std::unique_ptr<Asset> resources_asset,
- std::unique_ptr<AssetsProvider> assets,
+ApkAssetsPtr ApkAssets::LoadTable(std::unique_ptr<Asset>&& resources_asset,
+ std::unique_ptr<AssetsProvider>&& assets,
package_property_t flags) {
if (resources_asset == nullptr) {
return {};
@@ -97,10 +98,10 @@
std::move(loaded_idmap));
}
-ApkAssetsPtr ApkAssets::LoadImpl(std::unique_ptr<AssetsProvider> assets,
+ApkAssetsPtr ApkAssets::LoadImpl(std::unique_ptr<AssetsProvider>&& assets,
package_property_t property_flags,
- std::unique_ptr<Asset> idmap_asset,
- std::unique_ptr<LoadedIdmap> loaded_idmap) {
+ std::unique_ptr<Asset>&& idmap_asset,
+ std::unique_ptr<LoadedIdmap>&& loaded_idmap) {
if (assets == nullptr) {
return {};
}
@@ -119,11 +120,11 @@
std::move(idmap_asset), std::move(loaded_idmap));
}
-ApkAssetsPtr ApkAssets::LoadImpl(std::unique_ptr<Asset> resources_asset,
- std::unique_ptr<AssetsProvider> assets,
+ApkAssetsPtr ApkAssets::LoadImpl(std::unique_ptr<Asset>&& resources_asset,
+ std::unique_ptr<AssetsProvider>&& assets,
package_property_t property_flags,
- std::unique_ptr<Asset> idmap_asset,
- std::unique_ptr<LoadedIdmap> loaded_idmap) {
+ std::unique_ptr<Asset>&& idmap_asset,
+ std::unique_ptr<LoadedIdmap>&& loaded_idmap) {
if (assets == nullptr ) {
return {};
}
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index 5e645cc..a592749 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -38,7 +38,7 @@
}
CursorWindow::~CursorWindow() {
- if (mAshmemFd != -1) {
+ if (mAshmemFd >= 0) {
::munmap(mData, mSize);
::close(mAshmemFd);
} else {
@@ -155,23 +155,27 @@
bool isAshmem;
if (parcel->readBool(&isAshmem)) goto fail;
if (isAshmem) {
- window->mAshmemFd = parcel->readFileDescriptor();
- if (window->mAshmemFd < 0) {
+ int tempFd = parcel->readFileDescriptor();
+ if (tempFd < 0) {
LOG(ERROR) << "Failed readFileDescriptor";
goto fail_silent;
}
- window->mAshmemFd = ::fcntl(window->mAshmemFd, F_DUPFD_CLOEXEC, 0);
- if (window->mAshmemFd < 0) {
+ tempFd = ::fcntl(tempFd, F_DUPFD_CLOEXEC, 0);
+ if (tempFd < 0) {
PLOG(ERROR) << "Failed F_DUPFD_CLOEXEC";
goto fail_silent;
}
- window->mData = ::mmap(nullptr, window->mSize, PROT_READ, MAP_SHARED, window->mAshmemFd, 0);
+ window->mData = ::mmap(nullptr, window->mSize, PROT_READ, MAP_SHARED, tempFd, 0);
if (window->mData == MAP_FAILED) {
+ ::close(tempFd);
PLOG(ERROR) << "Failed mmap";
goto fail_silent;
}
+
+ window->mAshmemFd = tempFd;
+
} else {
window->mAshmemFd = -1;
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index 1fa6752..231808b 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -47,13 +47,37 @@
package_property_t flags = 0U, off64_t offset = 0,
off64_t len = AssetsProvider::kUnknownLength);
+ //
// Creates an ApkAssets from an AssetProvider.
- // The ApkAssets will take care of destroying the AssetsProvider when it is destroyed.
- static ApkAssetsPtr Load(std::unique_ptr<AssetsProvider> assets, package_property_t flags = 0U);
+ // The ApkAssets will take care of destroying the AssetsProvider when it is destroyed;
+ // the original argument is not moved from if loading fails.
+ //
+ // Note: this function takes care of the case when you pass a move(unique_ptr<Derived>)
+ // that would create a temporary unique_ptr<AssetsProvider> by moving your pointer into
+ // it before the function call, making it impossible to not move from the parameter
+ // on loading failure. The two overloads take care of moving the pointer back if needed.
+ //
+
+ template <class T>
+ static ApkAssetsPtr Load(std::unique_ptr<T>&& assets, package_property_t flags = 0U)
+ requires(std::is_same_v<T, AssetsProvider>) {
+ return LoadImpl(std::move(assets), flags);
+ }
+
+ template <class T>
+ static ApkAssetsPtr Load(std::unique_ptr<T>&& assets, package_property_t flags = 0U)
+ requires(!std::is_same_v<T, AssetsProvider> && std::is_base_of_v<AssetsProvider, T>) {
+ std::unique_ptr<AssetsProvider> base_assets(std::move(assets));
+ auto res = LoadImpl(std::move(base_assets), flags);
+ if (!res) {
+ assets.reset(static_cast<T*>(base_assets.release()));
+ }
+ return res;
+ }
// Creates an ApkAssets from the given asset file representing a resources.arsc.
- static ApkAssetsPtr LoadTable(std::unique_ptr<Asset> resources_asset,
- std::unique_ptr<AssetsProvider> assets,
+ static ApkAssetsPtr LoadTable(std::unique_ptr<Asset>&& resources_asset,
+ std::unique_ptr<AssetsProvider>&& assets,
package_property_t flags = 0U);
// Creates an ApkAssets from an IDMAP, which contains the original APK path, and the overlay
@@ -94,17 +118,29 @@
bool IsUpToDate() const;
- private:
- static ApkAssetsPtr LoadImpl(std::unique_ptr<AssetsProvider> assets,
- package_property_t property_flags,
- std::unique_ptr<Asset> idmap_asset,
- std::unique_ptr<LoadedIdmap> loaded_idmap);
+ // DANGER!
+ // This is a destructive method that rips the assets provider out of ApkAssets object.
+ // It is only useful when one knows this assets object can't be used anymore, and they
+ // need the underlying assets provider back (e.g. when initialization fails for some
+ // reason).
+ std::unique_ptr<AssetsProvider> TakeAssetsProvider() && {
+ return std::move(assets_provider_);
+ }
- static ApkAssetsPtr LoadImpl(std::unique_ptr<Asset> resources_asset,
- std::unique_ptr<AssetsProvider> assets,
+ private:
+ static ApkAssetsPtr LoadImpl(std::unique_ptr<AssetsProvider>&& assets,
package_property_t property_flags,
- std::unique_ptr<Asset> idmap_asset,
- std::unique_ptr<LoadedIdmap> loaded_idmap);
+ std::unique_ptr<Asset>&& idmap_asset,
+ std::unique_ptr<LoadedIdmap>&& loaded_idmap);
+
+ static ApkAssetsPtr LoadImpl(std::unique_ptr<Asset>&& resources_asset,
+ std::unique_ptr<AssetsProvider>&& assets,
+ package_property_t property_flags,
+ std::unique_ptr<Asset>&& idmap_asset,
+ std::unique_ptr<LoadedIdmap>&& loaded_idmap);
+
+ static ApkAssetsPtr LoadImpl(std::unique_ptr<AssetsProvider>&& assets,
+ package_property_t flags = 0U);
// Allows us to make it possible to call make_shared from inside the class but still keeps the
// ctor 'private' for all means and purposes.
diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp
index 70326b7..c36d990 100644
--- a/libs/androidfw/tests/ApkAssets_test.cpp
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -28,6 +28,7 @@
using ::com::android::basic::R;
using ::testing::Eq;
using ::testing::Ge;
+using ::testing::IsNull;
using ::testing::NotNull;
using ::testing::SizeIs;
using ::testing::StrEq;
@@ -108,4 +109,26 @@
EXPECT_THAT(buffer, StrEq("This should be uncompressed.\n\n"));
}
+TEST(ApkAssetsTest, TakeAssetsProviderNotCrashing) {
+ // Make sure the apk assets object can survive taking its assets provider and doesn't crash
+ // the process.
+ {
+ auto loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
+ ASSERT_THAT(loaded_apk, NotNull());
+
+ auto provider = std::move(*loaded_apk).TakeAssetsProvider();
+ ASSERT_THAT(provider, NotNull());
+ }
+ // If this test doesn't crash by this point we're all good.
+}
+
+TEST(ApkAssetsTest, AssetsProviderNotMovedOnError) {
+ auto assets_provider
+ = ZipAssetsProvider::Create(GetTestDataPath() + "/bad/bad.apk", 0);
+ ASSERT_THAT(assets_provider, NotNull());
+ auto loaded_apk = ApkAssets::Load(std::move(assets_provider));
+ ASSERT_THAT(loaded_apk, IsNull());
+ ASSERT_THAT(assets_provider, NotNull());
+}
+
} // namespace android
diff --git a/libs/androidfw/tests/data/bad/bad.apk b/libs/androidfw/tests/data/bad/bad.apk
new file mode 100644
index 0000000..3226bcd5
--- /dev/null
+++ b/libs/androidfw/tests/data/bad/bad.apk
Binary files differ
diff --git a/libs/appfunctions/api/current.txt b/libs/appfunctions/api/current.txt
index de40209..139ccfd 100644
--- a/libs/appfunctions/api/current.txt
+++ b/libs/appfunctions/api/current.txt
@@ -16,6 +16,7 @@
field public static final int ERROR_CATEGORY_UNKNOWN = 0; // 0x0
field public static final int ERROR_DENIED = 1000; // 0x3e8
field public static final int ERROR_DISABLED = 1002; // 0x3ea
+ field public static final int ERROR_ENTERPRISE_POLICY_DISALLOWED = 2002; // 0x7d2
field public static final int ERROR_FUNCTION_NOT_FOUND = 1003; // 0x3eb
field public static final int ERROR_INVALID_ARGUMENT = 1001; // 0x3e9
field public static final int ERROR_SYSTEM_ERROR = 2000; // 0x7d0
diff --git a/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionException.java b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionException.java
index 2540236..0c52169 100644
--- a/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionException.java
+++ b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionException.java
@@ -71,6 +71,13 @@
public static final int ERROR_CANCELLED = 2001;
/**
+ * The operation was disallowed by enterprise policy.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
+ */
+ public static final int ERROR_ENTERPRISE_POLICY_DISALLOWED = 2002;
+
+ /**
* An unknown error occurred while processing the call in the AppFunctionService.
*
* <p>This error is thrown when the service is connected in the remote application but an
@@ -189,7 +196,8 @@
ERROR_SYSTEM_ERROR,
ERROR_INVALID_ARGUMENT,
ERROR_DISABLED,
- ERROR_CANCELLED
+ ERROR_CANCELLED,
+ ERROR_ENTERPRISE_POLICY_DISALLOWED
})
@Retention(RetentionPolicy.SOURCE)
public @interface ErrorCode {}
diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp
index c735989..d1782b2 100644
--- a/libs/hwui/jni/text/TextShaper.cpp
+++ b/libs/hwui/jni/text/TextShaper.cpp
@@ -225,8 +225,8 @@
constexpr float NO_OVERRIDE = -1;
-float findValueFromVariationSettings(const minikin::FontFakery& fakery, minikin::AxisTag tag) {
- for (const minikin::FontVariation& fv : fakery.variationSettings()) {
+float findValueFromVariationSettings(const minikin::VariationSettings& axes, minikin::AxisTag tag) {
+ for (const minikin::FontVariation& fv : axes) {
if (fv.axisTag == tag) {
return fv.value;
}
@@ -238,8 +238,8 @@
static jfloat TextShaper_Result_getWeightOverride(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
if (text_feature::typeface_redesign_readonly()) {
- float value =
- findValueFromVariationSettings(layout->layout.getFakery(i), minikin::TAG_wght);
+ float value = findValueFromVariationSettings(layout->layout.typeface(i)->GetAxes(),
+ minikin::TAG_wght);
return std::isnan(value) ? NO_OVERRIDE : value;
} else {
return layout->layout.getFakery(i).wghtAdjustment();
@@ -250,8 +250,8 @@
static jfloat TextShaper_Result_getItalicOverride(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
if (text_feature::typeface_redesign_readonly()) {
- float value =
- findValueFromVariationSettings(layout->layout.getFakery(i), minikin::TAG_ital);
+ float value = findValueFromVariationSettings(layout->layout.typeface(i)->GetAxes(),
+ minikin::TAG_ital);
return std::isnan(value) ? NO_OVERRIDE : value;
} else {
return layout->layout.getFakery(i).italAdjustment();
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
index 28d96e3..65663d0 100644
--- a/libs/input/MouseCursorController.cpp
+++ b/libs/input/MouseCursorController.cpp
@@ -49,8 +49,7 @@
mLocked.lastFrameUpdatedTime = 0;
mLocked.pointerFadeDirection = 0;
- mLocked.pointerX = 0;
- mLocked.pointerY = 0;
+ mLocked.pointerPosition = {0, 0};
mLocked.pointerAlpha = 0.0f; // pointer is initially faded
mLocked.pointerSprite = mContext.getSpriteController().createSprite();
mLocked.updatePointerIcon = false;
@@ -66,11 +65,11 @@
mLocked.pointerSprite.clear();
}
-FloatPoint MouseCursorController::move(float deltaX, float deltaY) {
+vec2 MouseCursorController::move(vec2 delta) {
#if DEBUG_MOUSE_CURSOR_UPDATES
ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY);
#endif
- if (deltaX == 0.0f && deltaY == 0.0f) {
+ if (delta.x == 0.0f && delta.y == 0.0f) {
return {0, 0};
}
@@ -78,19 +77,19 @@
// if there's another display on the other side of the transition. At this point we still need
// to move the cursor to the boundary.
std::scoped_lock lock(mLock);
- const FloatPoint position{mLocked.pointerX + deltaX, mLocked.pointerY + deltaY};
- setPositionLocked(position.x, position.y);
+ const vec2 targetPosition = mLocked.pointerPosition + delta;
+ setPositionLocked(targetPosition);
// The amount of the delta that was not consumed as a result of the cursor
// hitting the edge of the display.
- return {position.x - mLocked.pointerX, position.y - mLocked.pointerY};
+ return targetPosition - mLocked.pointerPosition;
}
-void MouseCursorController::setPosition(float x, float y) {
+void MouseCursorController::setPosition(vec2 position) {
#if DEBUG_MOUSE_CURSOR_UPDATES
ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y);
#endif
std::scoped_lock lock(mLock);
- setPositionLocked(x, y);
+ setPositionLocked(position);
}
FloatRect MouseCursorController::getBoundsLocked() REQUIRES(mLock) {
@@ -105,21 +104,21 @@
};
}
-void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) {
+void MouseCursorController::setPositionLocked(vec2 position) REQUIRES(mLock) {
const auto& v = mLocked.viewport;
if (!v.isValid()) return;
const FloatRect bounds = getBoundsLocked();
- mLocked.pointerX = std::max(bounds.left, std::min(bounds.right, x));
- mLocked.pointerY = std::max(bounds.top, std::min(bounds.bottom, y));
+ mLocked.pointerPosition.x = std::max(bounds.left, std::min(bounds.right, position.x));
+ mLocked.pointerPosition.y = std::max(bounds.top, std::min(bounds.bottom, position.y));
updatePointerLocked();
}
-FloatPoint MouseCursorController::getPosition() const {
+vec2 MouseCursorController::getPosition() const {
std::scoped_lock lock(mLock);
- return {mLocked.pointerX, mLocked.pointerY};
+ return mLocked.pointerPosition;
}
ui::LogicalDisplayId MouseCursorController::getDisplayId() const {
@@ -221,20 +220,21 @@
if (viewport.isValid()) {
// Use integer coordinates as the starting point for the cursor location.
// We usually expect display sizes to be even numbers, so the flooring is precautionary.
- mLocked.pointerX = std::floor((viewport.logicalLeft + viewport.logicalRight) / 2);
- mLocked.pointerY = std::floor((viewport.logicalTop + viewport.logicalBottom) / 2);
+ mLocked.pointerPosition.x =
+ std::floor((viewport.logicalLeft + viewport.logicalRight) / 2);
+ mLocked.pointerPosition.y =
+ std::floor((viewport.logicalTop + viewport.logicalBottom) / 2);
// Reload icon resources for density may be changed.
loadResourcesLocked(getAdditionalMouseResources);
} else {
- mLocked.pointerX = 0;
- mLocked.pointerY = 0;
+ mLocked.pointerPosition = {0, 0};
}
} else if (oldViewport.orientation != viewport.orientation) {
// Apply offsets to convert from the pixel top-left corner position to the pixel center.
// This creates an invariant frame of reference that we can easily rotate when
// taking into account that the pointer may be located at fractional pixel offsets.
- float x = mLocked.pointerX + 0.5f;
- float y = mLocked.pointerY + 0.5f;
+ float x = mLocked.pointerPosition.x + 0.5f;
+ float y = mLocked.pointerPosition.y + 0.5f;
float temp;
// Undo the previous rotation.
@@ -279,8 +279,8 @@
// Apply offsets to convert from the pixel center to the pixel top-left corner position
// and save the results.
- mLocked.pointerX = x - 0.5f;
- mLocked.pointerY = y - 0.5f;
+ mLocked.pointerPosition.x = x - 0.5f;
+ mLocked.pointerPosition.y = y - 0.5f;
}
updatePointerLocked();
@@ -366,7 +366,7 @@
spriteController.openTransaction();
mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
- mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
+ mLocked.pointerSprite->setPosition(mLocked.pointerPosition.x, mLocked.pointerPosition.y);
mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
mLocked.pointerSprite->setSkipScreenshot(mLocked.skipScreenshot);
diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h
index e14a55d..7c674b5 100644
--- a/libs/input/MouseCursorController.h
+++ b/libs/input/MouseCursorController.h
@@ -41,9 +41,9 @@
~MouseCursorController();
// Move the pointer and return unconsumed delta
- FloatPoint move(float deltaX, float deltaY);
- void setPosition(float x, float y);
- FloatPoint getPosition() const;
+ vec2 move(vec2 delta);
+ void setPosition(vec2 position);
+ vec2 getPosition() const;
ui::LogicalDisplayId getDisplayId() const;
void fade(PointerControllerInterface::Transition transition);
void unfade(PointerControllerInterface::Transition transition);
@@ -81,8 +81,7 @@
nsecs_t lastFrameUpdatedTime;
int32_t pointerFadeDirection;
- float pointerX;
- float pointerY;
+ vec2 pointerPosition;
float pointerAlpha;
sp<Sprite> pointerSprite;
SpriteIcon pointerIcon;
@@ -101,7 +100,7 @@
} mLocked GUARDED_BY(mLock);
- void setPositionLocked(float x, float y);
+ void setPositionLocked(vec2 position);
void updatePointerLocked();
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index a713f1d..0b81211 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -138,7 +138,7 @@
return mDisplayInfoListener->mLock;
}
-FloatPoint PointerController::move(float deltaX, float deltaY) {
+vec2 PointerController::move(float deltaX, float deltaY) {
const ui::LogicalDisplayId displayId = mCursorController.getDisplayId();
ui::Transform transform;
{
@@ -147,10 +147,7 @@
}
const vec2 transformed = transformWithoutTranslation(transform, {deltaX, deltaY});
-
- const FloatPoint unconsumedDelta = mCursorController.move(transformed.x, transformed.y);
- return FloatPoint(transformWithoutTranslation(transform.inverse(),
- {unconsumedDelta.x, unconsumedDelta.y}));
+ return transformWithoutTranslation(transform.inverse(), mCursorController.move(transformed));
}
void PointerController::setPosition(float x, float y) {
@@ -161,16 +158,15 @@
const auto& transform = getTransformForDisplayLocked(displayId);
transformed = transform.transform(x, y);
}
- mCursorController.setPosition(transformed.x, transformed.y);
+ mCursorController.setPosition(transformed);
}
-FloatPoint PointerController::getPosition() const {
+vec2 PointerController::getPosition() const {
const ui::LogicalDisplayId displayId = mCursorController.getDisplayId();
const auto p = mCursorController.getPosition();
{
std::scoped_lock lock(getLock());
- const auto& transform = getTransformForDisplayLocked(displayId);
- return FloatPoint{transform.inverse().transform(p.x, p.y)};
+ return getTransformForDisplayLocked(displayId).inverse().transform(p.x, p.y);
}
}
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 8b33190..afd7215 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -51,9 +51,9 @@
~PointerController() override;
- FloatPoint move(float deltaX, float deltaY) override;
+ vec2 move(float deltaX, float deltaY) override;
void setPosition(float x, float y) override;
- FloatPoint getPosition() const override;
+ vec2 getPosition() const override;
ui::LogicalDisplayId getDisplayId() const override;
void fade(Transition transition) override;
void unfade(Transition transition) override;
@@ -166,13 +166,13 @@
~TouchPointerController() override;
- FloatPoint move(float, float) override {
+ vec2 move(float, float) override {
LOG_ALWAYS_FATAL("Should not be called");
}
void setPosition(float, float) override {
LOG_ALWAYS_FATAL("Should not be called");
}
- FloatPoint getPosition() const override {
+ vec2 getPosition() const override {
LOG_ALWAYS_FATAL("Should not be called");
}
ui::LogicalDisplayId getDisplayId() const override {
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
index 9478e35..ba42241 100644
--- a/location/api/system-current.txt
+++ b/location/api/system-current.txt
@@ -6,6 +6,111 @@
method public void onLocationBatch(java.util.List<android.location.Location>);
}
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class BeidouAssistance implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.location.GnssAlmanac getAlmanac();
+ method @Nullable public android.location.KlobucharIonosphericModel getIonosphericModel();
+ method @Nullable public android.location.LeapSecondsModel getLeapSecondsModel();
+ method @NonNull public java.util.List<android.location.RealTimeIntegrityModel> getRealTimeIntegrityModels();
+ method @NonNull public java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections> getSatelliteCorrections();
+ method @NonNull public java.util.List<android.location.BeidouSatelliteEphemeris> getSatelliteEphemeris();
+ method @NonNull public java.util.List<android.location.TimeModel> getTimeModels();
+ method @Nullable public android.location.UtcModel getUtcModel();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.BeidouAssistance> CREATOR;
+ }
+
+ public static final class BeidouAssistance.Builder {
+ ctor public BeidouAssistance.Builder();
+ method @NonNull public android.location.BeidouAssistance build();
+ method @NonNull public android.location.BeidouAssistance.Builder setAlmanac(@Nullable android.location.GnssAlmanac);
+ method @NonNull public android.location.BeidouAssistance.Builder setIonosphericModel(@Nullable android.location.KlobucharIonosphericModel);
+ method @NonNull public android.location.BeidouAssistance.Builder setLeapSecondsModel(@Nullable android.location.LeapSecondsModel);
+ method @NonNull public android.location.BeidouAssistance.Builder setRealTimeIntegrityModels(@Nullable java.util.List<android.location.RealTimeIntegrityModel>);
+ method @NonNull public android.location.BeidouAssistance.Builder setSatelliteCorrections(@Nullable java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
+ method @NonNull public android.location.BeidouAssistance.Builder setSatelliteEphemeris(@Nullable java.util.List<android.location.BeidouSatelliteEphemeris>);
+ method @NonNull public android.location.BeidouAssistance.Builder setTimeModels(@Nullable java.util.List<android.location.TimeModel>);
+ method @NonNull public android.location.BeidouAssistance.Builder setUtcModel(@Nullable android.location.UtcModel);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class BeidouSatelliteEphemeris implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=1, to=63) public int getPrn();
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel getSatelliteClockModel();
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime getSatelliteEphemerisTime();
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteHealth getSatelliteHealth();
+ method @NonNull public android.location.KeplerianOrbitModel getSatelliteOrbitModel();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.BeidouSatelliteEphemeris> CREATOR;
+ }
+
+ public static final class BeidouSatelliteEphemeris.BeidouSatelliteClockModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-0.00977F, to=0.00977f) public double getAf0();
+ method @FloatRange(from=-1.87E-9F, to=1.87E-9f) public double getAf1();
+ method @FloatRange(from=-1.39E-17F, to=1.39E-17f) public double getAf2();
+ method @IntRange(from=0, to=31) public int getAodc();
+ method @FloatRange(from=-5.12E-8F, to=5.12E-8f) public double getTgd1();
+ method @FloatRange(from=-5.12E-8F, to=5.12E-8f) public double getTgd2();
+ method @IntRange(from=0) public long getTimeOfClockSeconds();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel> CREATOR;
+ }
+
+ public static final class BeidouSatelliteEphemeris.BeidouSatelliteClockModel.Builder {
+ ctor public BeidouSatelliteEphemeris.BeidouSatelliteClockModel.Builder();
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel build();
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel.Builder setAf0(@FloatRange(from=-0.00977F, to=0.00977f) double);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel.Builder setAf1(@FloatRange(from=-1.87E-9F, to=1.87E-9f) double);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel.Builder setAf2(@FloatRange(from=-1.39E-17F, to=1.39E-17f) double);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel.Builder setAodc(@IntRange(from=0, to=31) int);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel.Builder setTgd1(@FloatRange(from=-5.12E-8F, to=5.12E-8f) double);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel.Builder setTgd2(@FloatRange(from=-5.12E-8F, to=5.12E-8f) double);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel.Builder setTimeOfClockSeconds(@IntRange(from=0) long);
+ }
+
+ public static final class BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0) public int getBeidouWeekNumber();
+ method @IntRange(from=0, to=31) public int getIode();
+ method @IntRange(from=0, to=604792) public int getToeSeconds();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime> CREATOR;
+ }
+
+ public static final class BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder {
+ ctor public BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder();
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime build();
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder setBeidouWeekNumber(@IntRange(from=0) int);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder setIode(int);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder setToeSeconds(@IntRange(from=0, to=604792) int);
+ }
+
+ public static final class BeidouSatelliteEphemeris.BeidouSatelliteHealth implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0, to=1) public int getSatH1();
+ method @FloatRange(from=0.0f, to=8192.0f) public double getSvAccur();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.BeidouSatelliteEphemeris.BeidouSatelliteHealth> CREATOR;
+ }
+
+ public static final class BeidouSatelliteEphemeris.BeidouSatelliteHealth.Builder {
+ ctor public BeidouSatelliteEphemeris.BeidouSatelliteHealth.Builder();
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteHealth build();
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteHealth.Builder setSatH1(int);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteHealth.Builder setSvAccur(double);
+ }
+
+ public static final class BeidouSatelliteEphemeris.Builder {
+ ctor public BeidouSatelliteEphemeris.Builder();
+ method @NonNull public android.location.BeidouSatelliteEphemeris build();
+ method @NonNull public android.location.BeidouSatelliteEphemeris.Builder setPrn(int);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.Builder setSatelliteClockModel(@NonNull android.location.BeidouSatelliteEphemeris.BeidouSatelliteClockModel);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.Builder setSatelliteEphemerisTime(@NonNull android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.Builder setSatelliteHealth(@NonNull android.location.BeidouSatelliteEphemeris.BeidouSatelliteHealth);
+ method @NonNull public android.location.BeidouSatelliteEphemeris.Builder setSatelliteOrbitModel(@NonNull android.location.KeplerianOrbitModel);
+ }
+
public final class CorrelationVector implements android.os.Parcelable {
method public int describeContents();
method @FloatRange(from=0.0f) public double getFrequencyOffsetMetersPerSecond();
@@ -43,12 +148,375 @@
method public void unregisterCountryDetectorCallback(@NonNull java.util.function.Consumer<android.location.Country>);
}
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GalileoAssistance implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.location.GnssAlmanac getAlmanac();
+ method @Nullable public android.location.KlobucharIonosphericModel getIonosphericModel();
+ method @Nullable public android.location.LeapSecondsModel getLeapSecondsModel();
+ method @NonNull public java.util.List<android.location.RealTimeIntegrityModel> getRealTimeIntegrityModels();
+ method @NonNull public java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections> getSatelliteCorrections();
+ method @NonNull public java.util.List<android.location.GalileoSatelliteEphemeris> getSatelliteEphemeris();
+ method @NonNull public java.util.List<android.location.TimeModel> getTimeModels();
+ method @Nullable public android.location.UtcModel getUtcModel();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GalileoAssistance> CREATOR;
+ }
+
+ public static final class GalileoAssistance.Builder {
+ ctor public GalileoAssistance.Builder();
+ method @NonNull public android.location.GalileoAssistance build();
+ method @NonNull public android.location.GalileoAssistance.Builder setAlmanac(@Nullable android.location.GnssAlmanac);
+ method @NonNull public android.location.GalileoAssistance.Builder setIonosphericModel(@Nullable android.location.KlobucharIonosphericModel);
+ method @NonNull public android.location.GalileoAssistance.Builder setLeapSecondsModel(@Nullable android.location.LeapSecondsModel);
+ method @NonNull public android.location.GalileoAssistance.Builder setRealTimeIntegrityModels(@Nullable java.util.List<android.location.RealTimeIntegrityModel>);
+ method @NonNull public android.location.GalileoAssistance.Builder setSatelliteCorrections(@Nullable java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
+ method @NonNull public android.location.GalileoAssistance.Builder setSatelliteEphemeris(@Nullable java.util.List<android.location.GalileoSatelliteEphemeris>);
+ method @NonNull public android.location.GalileoAssistance.Builder setTimeModels(@Nullable java.util.List<android.location.TimeModel>);
+ method @NonNull public android.location.GalileoAssistance.Builder setUtcModel(@Nullable android.location.UtcModel);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GalileoIonosphericModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=0.0f, to=512.0f) public double getAi0();
+ method @FloatRange(from=-4.0F, to=4.0f) public double getAi1();
+ method @FloatRange(from=-0.5F, to=0.5f) public double getAi2();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GalileoIonosphericModel> CREATOR;
+ }
+
+ public static final class GalileoIonosphericModel.Builder {
+ ctor public GalileoIonosphericModel.Builder();
+ method @NonNull public android.location.GalileoIonosphericModel build();
+ method @NonNull public android.location.GalileoIonosphericModel.Builder setAi0(@FloatRange(from=0.0f, to=512.0f) double);
+ method @NonNull public android.location.GalileoIonosphericModel.Builder setAi1(@FloatRange(from=-4.0F, to=4.0f) double);
+ method @NonNull public android.location.GalileoIonosphericModel.Builder setAi2(@FloatRange(from=-0.5F, to=0.5f) double);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GalileoSatelliteEphemeris implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel> getSatelliteClockModels();
+ method @IntRange(from=1, to=36) public int getSatelliteCodeNumber();
+ method @NonNull public android.location.SatelliteEphemerisTime getSatelliteEphemerisTime();
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth getSatelliteHealth();
+ method @NonNull public android.location.KeplerianOrbitModel getSatelliteOrbitModel();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GalileoSatelliteEphemeris> CREATOR;
+ }
+
+ public static final class GalileoSatelliteEphemeris.Builder {
+ ctor public GalileoSatelliteEphemeris.Builder();
+ method @NonNull public android.location.GalileoSatelliteEphemeris build();
+ method @NonNull public android.location.GalileoSatelliteEphemeris.Builder setSatelliteClockModels(@NonNull java.util.List<android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel>);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.Builder setSatelliteCodeNumber(@IntRange(from=1, to=36) int);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.Builder setSatelliteEphemerisTime(@NonNull android.location.SatelliteEphemerisTime);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.Builder setSatelliteHealth(@NonNull android.location.GalileoSatelliteEphemeris.GalileoSvHealth);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.Builder setSatelliteOrbitModel(@NonNull android.location.KeplerianOrbitModel);
+ }
+
+ public static final class GalileoSatelliteEphemeris.GalileoSatelliteClockModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-0.0625F, to=0.0625f) public double getAf0();
+ method @FloatRange(from=-1.5E-8F, to=1.5E-8f) public double getAf1();
+ method @FloatRange(from=-5.56E-17F, to=5.56E-17f) public double getAf2();
+ method @FloatRange(from=-1.2E-7F, to=1.2E-7f) public double getBgdSeconds();
+ method public int getSatelliteClockType();
+ method @FloatRange(from=0.0f) public double getSisaMeters();
+ method @IntRange(from=0) public long getTimeOfClockSeconds();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel> CREATOR;
+ field public static final int TYPE_FNAV = 1; // 0x1
+ field public static final int TYPE_INAV = 2; // 0x2
+ field public static final int TYPE_UNDEFINED = 0; // 0x0
+ }
+
+ public static final class GalileoSatelliteEphemeris.GalileoSatelliteClockModel.Builder {
+ ctor public GalileoSatelliteEphemeris.GalileoSatelliteClockModel.Builder();
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel build();
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel.Builder setAf0(@FloatRange(from=-0.0625F, to=0.0625f) double);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel.Builder setAf1(@FloatRange(from=-1.5E-8F, to=1.5E-8f) double);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel.Builder setAf2(@FloatRange(from=-5.56E-17F, to=5.56E-17f) double);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel.Builder setBgdSeconds(@FloatRange(from=-1.2E-7F, to=1.2E-7f) double);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel.Builder setSatelliteClockType(int);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel.Builder setSisaMeters(@FloatRange(from=0.0f) double);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSatelliteClockModel.Builder setTimeOfClockSeconds(@IntRange(from=0) long);
+ }
+
+ public static final class GalileoSatelliteEphemeris.GalileoSvHealth implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0, to=1) public int getDataValidityStatusE1b();
+ method @IntRange(from=0, to=1) public int getDataValidityStatusE5a();
+ method @IntRange(from=0, to=1) public int getDataValidityStatusE5b();
+ method @IntRange(from=0, to=3) public int getSignalHealthStatusE1b();
+ method @IntRange(from=0, to=3) public int getSignalHealthStatusE5a();
+ method @IntRange(from=0, to=3) public int getSignalHealthStatusE5b();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GalileoSatelliteEphemeris.GalileoSvHealth> CREATOR;
+ }
+
+ public static final class GalileoSatelliteEphemeris.GalileoSvHealth.Builder {
+ ctor public GalileoSatelliteEphemeris.GalileoSvHealth.Builder();
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth build();
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setDataValidityStatusE1b(@IntRange(from=0, to=1) int);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setDataValidityStatusE5a(@IntRange(from=0, to=1) int);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setDataValidityStatusE5b(@IntRange(from=0, to=1) int);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setSignalHealthStatusE1b(@IntRange(from=0, to=3) int);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setSignalHealthStatusE5a(@IntRange(from=0, to=3) int);
+ method @NonNull public android.location.GalileoSatelliteEphemeris.GalileoSvHealth.Builder setSignalHealthStatusE5b(@IntRange(from=0, to=3) int);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GlonassAlmanac implements android.os.Parcelable {
+ ctor public GlonassAlmanac(@IntRange(from=0) long, @NonNull java.util.List<android.location.GlonassAlmanac.GlonassSatelliteAlmanac>);
+ method public int describeContents();
+ method @IntRange(from=0) public long getIssueDateMillis();
+ method @NonNull public java.util.List<android.location.GlonassAlmanac.GlonassSatelliteAlmanac> getSatelliteAlmanacs();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GlonassAlmanac> CREATOR;
+ }
+
+ public static final class GlonassAlmanac.GlonassSatelliteAlmanac implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-0.067F, to=0.067f) public double getDeltaI();
+ method @FloatRange(from=-3600.0F, to=3600.0f) public double getDeltaT();
+ method @FloatRange(from=-0.004F, to=0.004f) public double getDeltaTDot();
+ method @FloatRange(from=0.0f, to=0.03f) public double getEccentricity();
+ method @IntRange(from=0, to=31) public int getFreqChannel();
+ method @FloatRange(from=-1.0F, to=1.0f) public double getLambda();
+ method @FloatRange(from=-1.0F, to=1.0f) public double getOmega();
+ method @IntRange(from=1, to=25) public int getSlotNumber();
+ method @IntRange(from=0, to=1) public int getSvHealth();
+ method @FloatRange(from=0.0f, to=44100.0f) public double getTLambda();
+ method @FloatRange(from=-0.0019F, to=0.0019f) public double getTau();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GlonassAlmanac.GlonassSatelliteAlmanac> CREATOR;
+ }
+
+ public static final class GlonassAlmanac.GlonassSatelliteAlmanac.Builder {
+ ctor public GlonassAlmanac.GlonassSatelliteAlmanac.Builder();
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac build();
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setDeltaI(@FloatRange(from=-0.067F, to=0.067f) double);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setDeltaT(@FloatRange(from=-3600.0F, to=3600.0f) double);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setDeltaTDot(@FloatRange(from=-0.004F, to=0.004f) double);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setEccentricity(@FloatRange(from=0.0f, to=0.03f) double);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setFreqChannel(@IntRange(from=0, to=31) int);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setLambda(@FloatRange(from=-1.0F, to=1.0f) double);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setOmega(@FloatRange(from=-1.0F, to=1.0f) double);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setSlotNumber(@IntRange(from=1, to=25) int);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setSvHealth(@IntRange(from=0, to=1) int);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setTLambda(@FloatRange(from=0.0f, to=44100.0f) double);
+ method @NonNull public android.location.GlonassAlmanac.GlonassSatelliteAlmanac.Builder setTau(@FloatRange(from=-0.0019F, to=0.0019f) double);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GlonassAssistance implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.location.GlonassAlmanac getAlmanac();
+ method @NonNull public java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections> getSatelliteCorrections();
+ method @NonNull public java.util.List<android.location.GlonassSatelliteEphemeris> getSatelliteEphemeris();
+ method @NonNull public java.util.List<android.location.TimeModel> getTimeModels();
+ method @Nullable public android.location.UtcModel getUtcModel();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GlonassAssistance> CREATOR;
+ }
+
+ public static final class GlonassAssistance.Builder {
+ ctor public GlonassAssistance.Builder();
+ method @NonNull public android.location.GlonassAssistance build();
+ method @NonNull public android.location.GlonassAssistance.Builder setAlmanac(@Nullable android.location.GlonassAlmanac);
+ method @NonNull public android.location.GlonassAssistance.Builder setSatelliteCorrections(@Nullable java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
+ method @NonNull public android.location.GlonassAssistance.Builder setSatelliteEphemeris(@Nullable java.util.List<android.location.GlonassSatelliteEphemeris>);
+ method @NonNull public android.location.GlonassAssistance.Builder setTimeModels(@Nullable java.util.List<android.location.TimeModel>);
+ method @NonNull public android.location.GlonassAssistance.Builder setUtcModel(@Nullable android.location.UtcModel);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GlonassSatelliteEphemeris implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0, to=31) public int getAgeInDays();
+ method @FloatRange(from=0.0f) public double getFrameTimeSeconds();
+ method @IntRange(from=0, to=1) public int getHealthState();
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel getSatelliteClockModel();
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel getSatelliteOrbitModel();
+ method @IntRange(from=1, to=25) public int getSlotNumber();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GlonassSatelliteEphemeris> CREATOR;
+ }
+
+ public static final class GlonassSatelliteEphemeris.Builder {
+ ctor public GlonassSatelliteEphemeris.Builder();
+ method @NonNull public android.location.GlonassSatelliteEphemeris build();
+ method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setAgeInDays(@IntRange(from=0, to=31) int);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setFrameTimeSeconds(@FloatRange(from=0.0f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setHealthState(@IntRange(from=0, to=1) int);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setSatelliteClockModel(@NonNull android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setSatelliteOrbitModel(@NonNull android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.Builder setSlotNumber(@IntRange(from=1, to=25) int);
+ }
+
+ public static final class GlonassSatelliteEphemeris.GlonassSatelliteClockModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-0.002F, to=0.002f) public double getClockBias();
+ method @FloatRange(from=-9.32E-10F, to=9.32E-10f) public double getFrequencyBias();
+ method @IntRange(from=0xfffffff9, to=6) public int getFrequencyNumber();
+ method @IntRange(from=0) public long getTimeOfClockSeconds();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel> CREATOR;
+ }
+
+ public static final class GlonassSatelliteEphemeris.GlonassSatelliteClockModel.Builder {
+ ctor public GlonassSatelliteEphemeris.GlonassSatelliteClockModel.Builder();
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel build();
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel.Builder setClockBias(@FloatRange(from=-0.002F, to=0.002f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel.Builder setFrequencyBias(@FloatRange(from=-9.32E-10F, to=9.32E-10f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel.Builder setFrequencyNumber(@IntRange(from=0xfffffff9, to=6) int);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteClockModel.Builder setTimeOfClockSeconds(@IntRange(from=0) long);
+ }
+
+ public static final class GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-27000.0F, to=27000.0f) public double getX();
+ method @FloatRange(from=-6.2E-9F, to=6.2E-9f) public double getXAccel();
+ method @FloatRange(from=-4.3F, to=4.3f) public double getXDot();
+ method @FloatRange(from=-27000.0F, to=27000.0f) public double getY();
+ method @FloatRange(from=-6.2E-9F, to=6.2E-9f) public double getYAccel();
+ method @FloatRange(from=-4.3F, to=4.3f) public double getYDot();
+ method @FloatRange(from=-27000.0F, to=27000.0f) public double getZ();
+ method @FloatRange(from=-6.2E-9F, to=6.2E-9f) public double getZAccel();
+ method @FloatRange(from=-4.3F, to=4.3f) public double getZDot();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel> CREATOR;
+ }
+
+ public static final class GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder {
+ ctor public GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder();
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel build();
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder setX(@FloatRange(from=-27000.0F, to=27000.0f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder setXAccel(@FloatRange(from=-6.2E-9F, to=6.2E-9f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder setXDot(@FloatRange(from=-4.3F, to=4.3f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder setY(@FloatRange(from=-27000.0F, to=27000.0f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder setYAccel(@FloatRange(from=-6.2E-9F, to=6.2E-9f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder setYDot(@FloatRange(from=-4.3F, to=4.3f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder setZ(@FloatRange(from=-27000.0F, to=27000.0f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder setZAccel(@FloatRange(from=-6.2E-9F, to=6.2E-9f) double);
+ method @NonNull public android.location.GlonassSatelliteEphemeris.GlonassSatelliteOrbitModel.Builder setZDot(@FloatRange(from=-4.3F, to=4.3f) double);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GnssAlmanac implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<android.location.GnssAlmanac.GnssSatelliteAlmanac> getGnssSatelliteAlmanacs();
+ method @IntRange(from=0) public int getIod();
+ method @IntRange(from=0) public long getIssueDateMillis();
+ method @IntRange(from=0, to=604800) public int getToaSeconds();
+ method @IntRange(from=0) public int getWeekNumber();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAlmanac> CREATOR;
+ }
+
+ public static final class GnssAlmanac.Builder {
+ ctor public GnssAlmanac.Builder();
+ method @NonNull public android.location.GnssAlmanac build();
+ method @NonNull public android.location.GnssAlmanac.Builder setGnssSatelliteAlmanacs(@NonNull java.util.List<android.location.GnssAlmanac.GnssSatelliteAlmanac>);
+ method @NonNull public android.location.GnssAlmanac.Builder setIod(@IntRange(from=0) int);
+ method @NonNull public android.location.GnssAlmanac.Builder setIssueDateMillis(@IntRange(from=0) long);
+ method @NonNull public android.location.GnssAlmanac.Builder setToaSeconds(@IntRange(from=0, to=604800) int);
+ method @NonNull public android.location.GnssAlmanac.Builder setWeekNumber(@IntRange(from=0) int);
+ }
+
+ public static final class GnssAlmanac.GnssSatelliteAlmanac implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-0.0625F, to=0.0625f) public double getAf0();
+ method @FloatRange(from=-1.5E-8F, to=1.5E-8f) public double getAf1();
+ method @FloatRange(from=0.0f) public double getEccentricity();
+ method @FloatRange(from=-1.0F, to=1.0f) public double getInclination();
+ method @FloatRange(from=-1.0F, to=1.0f) public double getM0();
+ method @FloatRange(from=-1.0F, to=1.0f) public double getOmega();
+ method @FloatRange(from=-1.0F, to=1.0f) public double getOmega0();
+ method @FloatRange(from=-1.0F, to=1.0f) public double getOmegaDot();
+ method @FloatRange(from=0.0f, to=8192.0f) public double getRootA();
+ method @IntRange(from=0) public int getSvHealth();
+ method @IntRange(from=1) public int getSvid();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAlmanac.GnssSatelliteAlmanac> CREATOR;
+ }
+
+ public static final class GnssAlmanac.GnssSatelliteAlmanac.Builder {
+ ctor public GnssAlmanac.GnssSatelliteAlmanac.Builder();
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac build();
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setAf0(@FloatRange(from=-0.0625F, to=0.0625f) double);
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setAf1(@FloatRange(from=-1.5E-8F, to=1.5E-8f) double);
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setEccentricity(@FloatRange(from=0.0f) double);
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setInclination(@FloatRange(from=-1.0F, to=1.0f) double);
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setM0(@FloatRange(from=-1.0F, to=1.0f) double);
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setOmega(@FloatRange(from=-1.0F, to=1.0f) double);
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setOmega0(@FloatRange(from=-1.0F, to=1.0f) double);
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setOmegaDot(@FloatRange(from=-1.0F, to=1.0f) double);
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setRootA(@FloatRange(from=0.0f, to=8192.0f) double);
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setSvHealth(@IntRange(from=0) int);
+ method @NonNull public android.location.GnssAlmanac.GnssSatelliteAlmanac.Builder setSvid(@IntRange(from=1) int);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GnssAssistance implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.location.BeidouAssistance getBeidouAssistance();
+ method @Nullable public android.location.GalileoAssistance getGalileoAssistance();
+ method @Nullable public android.location.GlonassAssistance getGlonassAssistance();
+ method @Nullable public android.location.GpsAssistance getGpsAssistance();
+ method @Nullable public android.location.QzssAssistance getQzssAssistance();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAssistance> CREATOR;
+ }
+
+ public static final class GnssAssistance.Builder {
+ ctor public GnssAssistance.Builder();
+ method @NonNull public android.location.GnssAssistance build();
+ method @NonNull public android.location.GnssAssistance.Builder setBeidouAssistance(@Nullable android.location.BeidouAssistance);
+ method @NonNull public android.location.GnssAssistance.Builder setGalileoAssistance(@Nullable android.location.GalileoAssistance);
+ method @NonNull public android.location.GnssAssistance.Builder setGlonassAssistance(@Nullable android.location.GlonassAssistance);
+ method @NonNull public android.location.GnssAssistance.Builder setGpsAssistance(@Nullable android.location.GpsAssistance);
+ method @NonNull public android.location.GnssAssistance.Builder setQzssAssistance(@Nullable android.location.QzssAssistance);
+ }
+
+ public static final class GnssAssistance.GnssSatelliteCorrections implements android.os.Parcelable {
+ ctor public GnssAssistance.GnssSatelliteCorrections(@IntRange(from=1, to=206) int, @NonNull java.util.List<android.location.IonosphericCorrection>);
+ method public int describeContents();
+ method @NonNull public java.util.List<android.location.IonosphericCorrection> getIonosphericCorrections();
+ method @IntRange(from=1, to=206) public int getSvid();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAssistance.GnssSatelliteCorrections> CREATOR;
+ }
+
public final class GnssCapabilities implements android.os.Parcelable {
method @Deprecated public boolean hasMeasurementCorrectionsReflectingPane();
method @Deprecated public boolean hasNavMessages();
method @Deprecated public boolean hasSatelliteBlacklist();
}
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GnssCorrectionComponent implements android.os.Parcelable {
+ ctor public GnssCorrectionComponent(@NonNull String, @NonNull android.location.GnssCorrectionComponent.GnssInterval, @NonNull android.location.GnssCorrectionComponent.PseudorangeCorrection);
+ method public int describeContents();
+ method @NonNull public android.location.GnssCorrectionComponent.PseudorangeCorrection getPseudorangeCorrection();
+ method @NonNull public String getSourceKey();
+ method @NonNull public android.location.GnssCorrectionComponent.GnssInterval getValidityInterval();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssCorrectionComponent> CREATOR;
+ }
+
+ public static final class GnssCorrectionComponent.GnssInterval implements android.os.Parcelable {
+ ctor public GnssCorrectionComponent.GnssInterval(@IntRange(from=0) long, @IntRange(from=0) long);
+ method public int describeContents();
+ method @IntRange(from=0) public long getEndMillisSinceGpsEpoch();
+ method @IntRange(from=0) public long getStartMillisSinceGpsEpoch();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssCorrectionComponent.GnssInterval> CREATOR;
+ }
+
+ public static final class GnssCorrectionComponent.PseudorangeCorrection implements android.os.Parcelable {
+ ctor public GnssCorrectionComponent.PseudorangeCorrection(double, double, double);
+ method public int describeContents();
+ method public double getCorrectionMeters();
+ method public double getCorrectionRateMetersPerSecond();
+ method @FloatRange(from=0.0f) public double getCorrectionUncertaintyMeters();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssCorrectionComponent.PseudorangeCorrection> CREATOR;
+ }
+
public final class GnssExcessPathInfo implements android.os.Parcelable {
method public int describeContents();
method @FloatRange(from=0.0f) public float getAttenuationDb();
@@ -193,6 +661,33 @@
method @NonNull public android.location.GnssSingleSatCorrection.Builder setSatelliteId(@IntRange(from=0) int);
}
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GpsAssistance implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.location.GnssAlmanac getAlmanac();
+ method @Nullable public android.location.KlobucharIonosphericModel getIonosphericModel();
+ method @Nullable public android.location.LeapSecondsModel getLeapSecondsModel();
+ method @NonNull public java.util.List<android.location.RealTimeIntegrityModel> getRealTimeIntegrityModels();
+ method @NonNull public java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections> getSatelliteCorrections();
+ method @NonNull public java.util.List<android.location.GpsSatelliteEphemeris> getSatelliteEphemeris();
+ method @NonNull public java.util.List<android.location.TimeModel> getTimeModels();
+ method @Nullable public android.location.UtcModel getUtcModel();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsAssistance> CREATOR;
+ }
+
+ public static final class GpsAssistance.Builder {
+ ctor public GpsAssistance.Builder();
+ method @NonNull public android.location.GpsAssistance build();
+ method @NonNull public android.location.GpsAssistance.Builder setAlmanac(@Nullable android.location.GnssAlmanac);
+ method @NonNull public android.location.GpsAssistance.Builder setIonosphericModel(@Nullable android.location.KlobucharIonosphericModel);
+ method @NonNull public android.location.GpsAssistance.Builder setLeapSecondsModel(@Nullable android.location.LeapSecondsModel);
+ method @NonNull public android.location.GpsAssistance.Builder setRealTimeIntegrityModels(@Nullable java.util.List<android.location.RealTimeIntegrityModel>);
+ method @NonNull public android.location.GpsAssistance.Builder setSatelliteCorrections(@Nullable java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
+ method @NonNull public android.location.GpsAssistance.Builder setSatelliteEphemeris(@Nullable java.util.List<android.location.GpsSatelliteEphemeris>);
+ method @NonNull public android.location.GpsAssistance.Builder setTimeModels(@Nullable java.util.List<android.location.TimeModel>);
+ method @NonNull public android.location.GpsAssistance.Builder setUtcModel(@Nullable android.location.UtcModel);
+ }
+
@Deprecated public class GpsClock implements android.os.Parcelable {
method @Deprecated public int describeContents();
method @Deprecated public double getBiasInNs();
@@ -418,6 +913,174 @@
method @Deprecated public void onStatusChanged(int);
}
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class GpsSatelliteEphemeris implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsL2Params getGpsL2Params();
+ method @IntRange(from=1, to=32) public int getPrn();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel getSatelliteClockModel();
+ method @NonNull public android.location.SatelliteEphemerisTime getSatelliteEphemerisTime();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteHealth getSatelliteHealth();
+ method @NonNull public android.location.KeplerianOrbitModel getSatelliteOrbitModel();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsSatelliteEphemeris> CREATOR;
+ }
+
+ public static final class GpsSatelliteEphemeris.Builder {
+ ctor public GpsSatelliteEphemeris.Builder();
+ method @NonNull public android.location.GpsSatelliteEphemeris build();
+ method @NonNull public android.location.GpsSatelliteEphemeris.Builder setGpsL2Params(@NonNull android.location.GpsSatelliteEphemeris.GpsL2Params);
+ method @NonNull public android.location.GpsSatelliteEphemeris.Builder setPrn(@IntRange(from=1, to=32) int);
+ method @NonNull public android.location.GpsSatelliteEphemeris.Builder setSatelliteClockModel(@NonNull android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel);
+ method @NonNull public android.location.GpsSatelliteEphemeris.Builder setSatelliteEphemerisTime(@NonNull android.location.SatelliteEphemerisTime);
+ method @NonNull public android.location.GpsSatelliteEphemeris.Builder setSatelliteHealth(@NonNull android.location.GpsSatelliteEphemeris.GpsSatelliteHealth);
+ method @NonNull public android.location.GpsSatelliteEphemeris.Builder setSatelliteOrbitModel(@NonNull android.location.KeplerianOrbitModel);
+ }
+
+ public static final class GpsSatelliteEphemeris.GpsL2Params implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0, to=3) public int getL2Code();
+ method @IntRange(from=0, to=1) public int getL2Flag();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsSatelliteEphemeris.GpsL2Params> CREATOR;
+ }
+
+ public static final class GpsSatelliteEphemeris.GpsL2Params.Builder {
+ ctor public GpsSatelliteEphemeris.GpsL2Params.Builder();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsL2Params build();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsL2Params.Builder setL2Code(@IntRange(from=0, to=3) int);
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsL2Params.Builder setL2Flag(@IntRange(from=0, to=1) int);
+ }
+
+ public static final class GpsSatelliteEphemeris.GpsSatelliteClockModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-0.00977F, to=0.00977f) public double getAf0();
+ method @FloatRange(from=-3.73E-9F, to=3.73E-9f) public double getAf1();
+ method @FloatRange(from=-3.56E-15F, to=3.56E-15f) public double getAf2();
+ method @IntRange(from=0, to=1023) public int getIodc();
+ method @FloatRange(from=-5.97E-8F, to=5.97E-8f) public double getTgd();
+ method @IntRange(from=0) public long getTimeOfClockSeconds();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel> CREATOR;
+ }
+
+ public static final class GpsSatelliteEphemeris.GpsSatelliteClockModel.Builder {
+ ctor public GpsSatelliteEphemeris.GpsSatelliteClockModel.Builder();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel build();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel.Builder setAf0(@FloatRange(from=-0.00977F, to=0.00977f) double);
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel.Builder setAf1(@FloatRange(from=-3.73E-9F, to=3.73E-9f) double);
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel.Builder setAf2(@FloatRange(from=-3.56E-15F, to=3.56E-15f) double);
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel.Builder setIodc(@IntRange(from=0, to=1023) int);
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel.Builder setTgd(@FloatRange(from=-5.97E-8F, to=5.97E-8f) double);
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel.Builder setTimeOfClockSeconds(@IntRange(from=0) long);
+ }
+
+ public static final class GpsSatelliteEphemeris.GpsSatelliteHealth implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=0.0f) public double getFitInt();
+ method @FloatRange(from=0.0f, to=8192.0f) public double getSvAccur();
+ method @IntRange(from=0, to=63) public int getSvHealth();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsSatelliteEphemeris.GpsSatelliteHealth> CREATOR;
+ }
+
+ public static final class GpsSatelliteEphemeris.GpsSatelliteHealth.Builder {
+ ctor public GpsSatelliteEphemeris.GpsSatelliteHealth.Builder();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteHealth build();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteHealth.Builder setFitInt(@FloatRange(from=0.0f) double);
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteHealth.Builder setSvAccur(@FloatRange(from=0.0f, to=8192.0f) double);
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteHealth.Builder setSvHealth(@IntRange(from=0, to=63) int);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class IonosphericCorrection implements android.os.Parcelable {
+ ctor public IonosphericCorrection(@IntRange(from=0) long, @NonNull android.location.GnssCorrectionComponent);
+ method public int describeContents();
+ method @IntRange(from=0) public long getCarrierFrequencyHz();
+ method @NonNull public android.location.GnssCorrectionComponent getIonosphericCorrection();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.IonosphericCorrection> CREATOR;
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class KeplerianOrbitModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-1.18E-8F, to=1.18E-8f) public double getDeltaN();
+ method @FloatRange(from=0.0f, to=0.5f) public double getEccentricity();
+ method @FloatRange(from=-3.15F, to=3.15f) public double getI0();
+ method @FloatRange(from=-2.94E-9F, to=2.94E-9f) public double getIDot();
+ method @FloatRange(from=-3.15F, to=3.15f) public double getM0();
+ method @FloatRange(from=-3.15F, to=3.15f) public double getOmega();
+ method @FloatRange(from=-3.15F, to=3.15f) public double getOmega0();
+ method @FloatRange(from=-3.1E-6F, to=3.1E-6f) public double getOmegaDot();
+ method @FloatRange(from=0.0f, to=8192.0f) public double getRootA();
+ method @NonNull public android.location.KeplerianOrbitModel.SecondOrderHarmonicPerturbation getSecondOrderHarmonicPerturbation();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.KeplerianOrbitModel> CREATOR;
+ }
+
+ public static final class KeplerianOrbitModel.Builder {
+ ctor public KeplerianOrbitModel.Builder();
+ method @NonNull public android.location.KeplerianOrbitModel build();
+ method @NonNull public android.location.KeplerianOrbitModel.Builder setDeltaN(@FloatRange(from=-1.18E-8F, to=1.18E-8f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.Builder setEccentricity(@FloatRange(from=0.0f, to=0.5f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.Builder setI0(@FloatRange(from=-3.15F, to=3.15f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.Builder setIDot(@FloatRange(from=-2.94E-9F, to=2.94E-9f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.Builder setM0(@FloatRange(from=-3.15F, to=3.15f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.Builder setOmega(@FloatRange(from=-3.15F, to=3.15f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.Builder setOmega0(@FloatRange(from=-3.15F, to=3.15f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.Builder setOmegaDot(@FloatRange(from=-3.1E-6F, to=3.1E-6f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.Builder setRootA(@FloatRange(from=0.0f, to=8192.0f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.Builder setSecondOrderHarmonicPerturbation(@NonNull android.location.KeplerianOrbitModel.SecondOrderHarmonicPerturbation);
+ }
+
+ public static final class KeplerianOrbitModel.SecondOrderHarmonicPerturbation implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-6.11E-5F, to=6.11E-5f) public double getCic();
+ method @FloatRange(from=-6.11E-5F, to=6.11E-5f) public double getCis();
+ method @FloatRange(from=-2048.0F, to=2048.0f) public double getCrc();
+ method @FloatRange(from=-2048.0F, to=2048.0f) public double getCrs();
+ method @FloatRange(from=-6.11E-5F, to=6.11E-5f) public double getCuc();
+ method @FloatRange(from=-6.11E-5F, to=6.11E-5f) public double getCus();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.KeplerianOrbitModel.SecondOrderHarmonicPerturbation> CREATOR;
+ }
+
+ public static final class KeplerianOrbitModel.SecondOrderHarmonicPerturbation.Builder {
+ ctor public KeplerianOrbitModel.SecondOrderHarmonicPerturbation.Builder();
+ method @NonNull public android.location.KeplerianOrbitModel.SecondOrderHarmonicPerturbation build();
+ method @NonNull public android.location.KeplerianOrbitModel.SecondOrderHarmonicPerturbation.Builder setCic(@FloatRange(from=-6.11E-5F, to=6.11E-5f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.SecondOrderHarmonicPerturbation.Builder setCis(@FloatRange(from=-6.11E-5F, to=6.11E-5f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.SecondOrderHarmonicPerturbation.Builder setCrc(@FloatRange(from=-2048.0F, to=2048.0f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.SecondOrderHarmonicPerturbation.Builder setCrs(@FloatRange(from=-2048.0F, to=2048.0f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.SecondOrderHarmonicPerturbation.Builder setCuc(@FloatRange(from=-6.11E-5F, to=6.11E-5f) double);
+ method @NonNull public android.location.KeplerianOrbitModel.SecondOrderHarmonicPerturbation.Builder setCus(@FloatRange(from=-6.11E-5F, to=6.11E-5f) double);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class KlobucharIonosphericModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-1.193E-7F, to=1.193E-7f) public double getAlpha0();
+ method @FloatRange(from=-9.54E-7F, to=9.54E-7f) public double getAlpha1();
+ method @FloatRange(from=-7.63E-6F, to=7.63E-6f) public double getAlpha2();
+ method @FloatRange(from=-7.63E-6F, to=7.63E-6f) public double getAlpha3();
+ method @FloatRange(from=-262144.0F, to=262144.0f) public double getBeta0();
+ method @FloatRange(from=-2097152.0F, to=2097152.0f) public double getBeta1();
+ method @FloatRange(from=-8388608.0F, to=8388608.0f) public double getBeta2();
+ method @FloatRange(from=-8388608.0F, to=8388608.0f) public double getBeta3();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.KlobucharIonosphericModel> CREATOR;
+ }
+
+ public static final class KlobucharIonosphericModel.Builder {
+ ctor public KlobucharIonosphericModel.Builder();
+ method @NonNull public android.location.KlobucharIonosphericModel build();
+ method @NonNull public android.location.KlobucharIonosphericModel.Builder setAlpha0(@FloatRange(from=-1.193E-7F, to=1.193E-7f) double);
+ method @NonNull public android.location.KlobucharIonosphericModel.Builder setAlpha1(@FloatRange(from=-9.54E-7F, to=9.54E-7f) double);
+ method @NonNull public android.location.KlobucharIonosphericModel.Builder setAlpha2(@FloatRange(from=-7.63E-6F, to=7.63E-6f) double);
+ method @NonNull public android.location.KlobucharIonosphericModel.Builder setAlpha3(@FloatRange(from=-7.63E-6F, to=7.63E-6f) double);
+ method @NonNull public android.location.KlobucharIonosphericModel.Builder setBeta0(@FloatRange(from=-262144.0F, to=262144.0f) double);
+ method @NonNull public android.location.KlobucharIonosphericModel.Builder setBeta1(@FloatRange(from=-2097152.0F, to=2097152.0f) double);
+ method @NonNull public android.location.KlobucharIonosphericModel.Builder setBeta2(@FloatRange(from=-8388608.0F, to=8388608.0f) double);
+ method @NonNull public android.location.KlobucharIonosphericModel.Builder setBeta3(@FloatRange(from=-8388608.0F, to=8388608.0f) double);
+ }
+
public final class LastLocationRequest implements android.os.Parcelable {
method public int describeContents();
method public boolean isAdasGnssBypass();
@@ -436,6 +1099,25 @@
method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_BYPASS) public android.location.LastLocationRequest.Builder setLocationSettingsIgnored(boolean);
}
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class LeapSecondsModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0) public int getDayNumberLeapSecondsFuture();
+ method @IntRange(from=0) public int getLeapSeconds();
+ method @IntRange(from=0) public int getLeapSecondsFuture();
+ method @IntRange(from=0) public int getWeekNumberLeapSecondsFuture();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.LeapSecondsModel> CREATOR;
+ }
+
+ public static final class LeapSecondsModel.Builder {
+ ctor public LeapSecondsModel.Builder();
+ method @NonNull public android.location.LeapSecondsModel build();
+ method @NonNull public android.location.LeapSecondsModel.Builder setDayNumberLeapSecondsFuture(@IntRange(from=0) int);
+ method @NonNull public android.location.LeapSecondsModel.Builder setLeapSeconds(@IntRange(from=0) int);
+ method @NonNull public android.location.LeapSecondsModel.Builder setLeapSecondsFuture(@IntRange(from=0) int);
+ method @NonNull public android.location.LeapSecondsModel.Builder setWeekNumberLeapSecondsFuture(@IntRange(from=0) int);
+ }
+
public class LocationManager {
method @Deprecated @FlaggedApi("android.location.flags.deprecate_provider_request_apis") @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void addProviderRequestChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.ChangedListener);
method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void flushGnssBatch();
@@ -513,6 +1195,98 @@
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
}
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class QzssAssistance implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.location.GnssAlmanac getAlmanac();
+ method @Nullable public android.location.KlobucharIonosphericModel getIonosphericModel();
+ method @Nullable public android.location.LeapSecondsModel getLeapSecondsModel();
+ method @NonNull public java.util.List<android.location.RealTimeIntegrityModel> getRealTimeIntegrityModels();
+ method @NonNull public java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections> getSatelliteCorrections();
+ method @NonNull public java.util.List<android.location.QzssSatelliteEphemeris> getSatelliteEphemeris();
+ method @NonNull public java.util.List<android.location.TimeModel> getTimeModels();
+ method @Nullable public android.location.UtcModel getUtcModel();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.QzssAssistance> CREATOR;
+ }
+
+ public static final class QzssAssistance.Builder {
+ ctor public QzssAssistance.Builder();
+ method @NonNull public android.location.QzssAssistance build();
+ method @NonNull public android.location.QzssAssistance.Builder setAlmanac(@Nullable android.location.GnssAlmanac);
+ method @NonNull public android.location.QzssAssistance.Builder setIonosphericModel(@Nullable android.location.KlobucharIonosphericModel);
+ method @NonNull public android.location.QzssAssistance.Builder setLeapSecondsModel(@Nullable android.location.LeapSecondsModel);
+ method @NonNull public android.location.QzssAssistance.Builder setRealTimeIntegrityModels(@Nullable java.util.List<android.location.RealTimeIntegrityModel>);
+ method @NonNull public android.location.QzssAssistance.Builder setSatelliteCorrections(@Nullable java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
+ method @NonNull public android.location.QzssAssistance.Builder setSatelliteEphemeris(@Nullable java.util.List<android.location.QzssSatelliteEphemeris>);
+ method @NonNull public android.location.QzssAssistance.Builder setTimeModels(@Nullable java.util.List<android.location.TimeModel>);
+ method @NonNull public android.location.QzssAssistance.Builder setUtcModel(@Nullable android.location.UtcModel);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class QzssSatelliteEphemeris implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsL2Params getGpsL2Params();
+ method @IntRange(from=183, to=206) public int getPrn();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel getSatelliteClockModel();
+ method @NonNull public android.location.SatelliteEphemerisTime getSatelliteEphemerisTime();
+ method @NonNull public android.location.GpsSatelliteEphemeris.GpsSatelliteHealth getSatelliteHealth();
+ method @NonNull public android.location.KeplerianOrbitModel getSatelliteOrbitModel();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.QzssSatelliteEphemeris> CREATOR;
+ }
+
+ public static final class QzssSatelliteEphemeris.Builder {
+ ctor public QzssSatelliteEphemeris.Builder();
+ method @NonNull public android.location.QzssSatelliteEphemeris build();
+ method @NonNull public android.location.QzssSatelliteEphemeris.Builder setGpsL2Params(@NonNull android.location.GpsSatelliteEphemeris.GpsL2Params);
+ method @NonNull public android.location.QzssSatelliteEphemeris.Builder setPrn(@IntRange(from=183, to=206) int);
+ method @NonNull public android.location.QzssSatelliteEphemeris.Builder setSatelliteClockModel(@NonNull android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel);
+ method @NonNull public android.location.QzssSatelliteEphemeris.Builder setSatelliteEphemerisTime(@NonNull android.location.SatelliteEphemerisTime);
+ method @NonNull public android.location.QzssSatelliteEphemeris.Builder setSatelliteHealth(@NonNull android.location.GpsSatelliteEphemeris.GpsSatelliteHealth);
+ method @NonNull public android.location.QzssSatelliteEphemeris.Builder setSatelliteOrbitModel(@NonNull android.location.KeplerianOrbitModel);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class RealTimeIntegrityModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public String getAdvisoryNumber();
+ method @NonNull public String getAdvisoryType();
+ method @IntRange(from=0) public long getEndDateSeconds();
+ method @IntRange(from=0) public long getPublishDateSeconds();
+ method @IntRange(from=0) public long getStartDateSeconds();
+ method @IntRange(from=1, to=206) public int getSvid();
+ method public boolean isUsable();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.RealTimeIntegrityModel> CREATOR;
+ }
+
+ public static final class RealTimeIntegrityModel.Builder {
+ ctor public RealTimeIntegrityModel.Builder();
+ method @NonNull public android.location.RealTimeIntegrityModel build();
+ method @NonNull public android.location.RealTimeIntegrityModel.Builder setAdvisoryNumber(@NonNull String);
+ method @NonNull public android.location.RealTimeIntegrityModel.Builder setAdvisoryType(@NonNull String);
+ method @NonNull public android.location.RealTimeIntegrityModel.Builder setEndDateSeconds(@IntRange(from=0) long);
+ method @NonNull public android.location.RealTimeIntegrityModel.Builder setPublishDateSeconds(@IntRange(from=0) long);
+ method @NonNull public android.location.RealTimeIntegrityModel.Builder setStartDateSeconds(@IntRange(from=0) long);
+ method @NonNull public android.location.RealTimeIntegrityModel.Builder setSvid(@IntRange(from=1, to=206) int);
+ method @NonNull public android.location.RealTimeIntegrityModel.Builder setUsable(boolean);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class SatelliteEphemerisTime implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0, to=1023) public int getIode();
+ method @IntRange(from=0, to=604799) public int getToeSeconds();
+ method @IntRange(from=0) public int getWeekNumber();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.SatelliteEphemerisTime> CREATOR;
+ }
+
+ public static final class SatelliteEphemerisTime.Builder {
+ ctor public SatelliteEphemerisTime.Builder();
+ method @NonNull public android.location.SatelliteEphemerisTime build();
+ method @NonNull public android.location.SatelliteEphemerisTime.Builder setIode(@IntRange(from=0, to=1023) int);
+ method @NonNull public android.location.SatelliteEphemerisTime.Builder setToeSeconds(@IntRange(from=0, to=604799) int);
+ method @NonNull public android.location.SatelliteEphemerisTime.Builder setWeekNumber(@IntRange(from=0) int);
+ }
+
public final class SatellitePvt implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.location.SatellitePvt.ClockInfo getClockInfo();
@@ -587,6 +1361,46 @@
field @NonNull public static final android.os.Parcelable.Creator<android.location.SatellitePvt.VelocityEcef> CREATOR;
}
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class TimeModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-1.0F, to=1.0f) public double getA0();
+ method @FloatRange(from=-3.28E-6F, to=3.28E-6f) public double getA1();
+ method @IntRange(from=0, to=604800) public int getTimeOfWeek();
+ method public int getToGnss();
+ method @IntRange(from=0) public int getWeekNumber();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.TimeModel> CREATOR;
+ }
+
+ public static final class TimeModel.Builder {
+ ctor public TimeModel.Builder();
+ method @NonNull public android.location.TimeModel build();
+ method @NonNull public android.location.TimeModel.Builder setA0(@FloatRange(from=-1.0F, to=1.0f) double);
+ method @NonNull public android.location.TimeModel.Builder setA1(@FloatRange(from=-3.28E-6F, to=3.28E-6f) double);
+ method @NonNull public android.location.TimeModel.Builder setTimeOfWeek(@IntRange(from=0, to=604800) int);
+ method @NonNull public android.location.TimeModel.Builder setToGnss(int);
+ method @NonNull public android.location.TimeModel.Builder setWeekNumber(@IntRange(from=0) int);
+ }
+
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public final class UtcModel implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=-2.0F, to=2.0f) public double getA0();
+ method @FloatRange(from=-7.45E-9F, to=7.45E-9f) public double getA1();
+ method @IntRange(from=0, to=604800) public int getTimeOfWeek();
+ method @IntRange(from=0) public int getWeekNumber();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.UtcModel> CREATOR;
+ }
+
+ public static final class UtcModel.Builder {
+ ctor public UtcModel.Builder();
+ method @NonNull public android.location.UtcModel build();
+ method @NonNull public android.location.UtcModel.Builder setA0(@FloatRange(from=-2.0F, to=2.0f) double);
+ method @NonNull public android.location.UtcModel.Builder setA1(@FloatRange(from=-7.45E-9F, to=7.45E-9f) double);
+ method @NonNull public android.location.UtcModel.Builder setTimeOfWeek(@IntRange(from=0, to=604800) int);
+ method @NonNull public android.location.UtcModel.Builder setWeekNumber(@IntRange(from=0) int);
+ }
+
}
package android.location.provider {
diff --git a/location/java/android/location/BeidouAssistance.java b/location/java/android/location/BeidouAssistance.java
new file mode 100644
index 0000000..f55249e6
--- /dev/null
+++ b/location/java/android/location/BeidouAssistance.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.location.GnssAssistance.GnssSatelliteCorrections;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class contains Beidou assistance.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class BeidouAssistance implements Parcelable {
+
+ /** The Beidou almanac. */
+ @Nullable private final GnssAlmanac mAlmanac;
+
+ /** The Klobuchar ionospheric model. */
+ @Nullable private final KlobucharIonosphericModel mIonosphericModel;
+
+ /** The UTC model. */
+ @Nullable private final UtcModel mUtcModel;
+
+ /** The leap seconds model. */
+ @Nullable private final LeapSecondsModel mLeapSecondsModel;
+
+ /** The list of time models. */
+ @NonNull private final List<TimeModel> mTimeModels;
+
+ /** The list of Beidou ephemeris. */
+ @NonNull private final List<BeidouSatelliteEphemeris> mSatelliteEphemeris;
+
+ /** The list of real time integrity models. */
+ @NonNull private final List<RealTimeIntegrityModel> mRealTimeIntegrityModels;
+
+ /** The list of Beidou satellite corrections. */
+ @NonNull private final List<GnssSatelliteCorrections> mSatelliteCorrections;
+
+ private BeidouAssistance(Builder builder) {
+ mAlmanac = builder.mAlmanac;
+ mIonosphericModel = builder.mIonosphericModel;
+ mUtcModel = builder.mUtcModel;
+ mLeapSecondsModel = builder.mLeapSecondsModel;
+ if (builder.mTimeModels != null) {
+ mTimeModels = Collections.unmodifiableList(new ArrayList<>(builder.mTimeModels));
+ } else {
+ mTimeModels = new ArrayList<>();
+ }
+ if (builder.mSatelliteEphemeris != null) {
+ mSatelliteEphemeris =
+ Collections.unmodifiableList(new ArrayList<>(builder.mSatelliteEphemeris));
+ } else {
+ mSatelliteEphemeris = new ArrayList<>();
+ }
+ if (builder.mRealTimeIntegrityModels != null) {
+ mRealTimeIntegrityModels =
+ Collections.unmodifiableList(new ArrayList<>(builder.mRealTimeIntegrityModels));
+ } else {
+ mRealTimeIntegrityModels = new ArrayList<>();
+ }
+ if (builder.mSatelliteCorrections != null) {
+ mSatelliteCorrections =
+ Collections.unmodifiableList(new ArrayList<>(builder.mSatelliteCorrections));
+ } else {
+ mSatelliteCorrections = new ArrayList<>();
+ }
+ }
+
+ /** Returns the Beidou almanac. */
+ @Nullable
+ public GnssAlmanac getAlmanac() {
+ return mAlmanac;
+ }
+
+ /** Returns the Klobuchar ionospheric model. */
+ @Nullable
+ public KlobucharIonosphericModel getIonosphericModel() {
+ return mIonosphericModel;
+ }
+
+ /** Returns the UTC model. */
+ @Nullable
+ public UtcModel getUtcModel() {
+ return mUtcModel;
+ }
+
+ /** Returns the leap seconds model. */
+ @Nullable
+ public LeapSecondsModel getLeapSecondsModel() {
+ return mLeapSecondsModel;
+ }
+
+ /** Returns the list of time models. */
+ @NonNull
+ public List<TimeModel> getTimeModels() {
+ return mTimeModels;
+ }
+
+ /** Returns the list ofBeidou ephemeris. */
+ @NonNull
+ public List<BeidouSatelliteEphemeris> getSatelliteEphemeris() {
+ return mSatelliteEphemeris;
+ }
+
+ /** Returns the list of real time integrity models. */
+ @NonNull
+ public List<RealTimeIntegrityModel> getRealTimeIntegrityModels() {
+ return mRealTimeIntegrityModels;
+ }
+
+ /** Returns the list of Beidou satellite corrections. */
+ @NonNull
+ public List<GnssSatelliteCorrections> getSatelliteCorrections() {
+ return mSatelliteCorrections;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("BeidouAssistance[");
+ builder.append("almanac = ").append(mAlmanac);
+ builder.append(", ionosphericModel = ").append(mIonosphericModel);
+ builder.append(", utcModel = ").append(mUtcModel);
+ builder.append(", leapSecondsModel = ").append(mLeapSecondsModel);
+ builder.append(", timeModels = ").append(mTimeModels);
+ builder.append(", satelliteEphemeris = ").append(mSatelliteEphemeris);
+ builder.append(", realTimeIntegrityModels = ").append(mRealTimeIntegrityModels);
+ builder.append(", satelliteCorrections = ").append(mSatelliteCorrections);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(mAlmanac, flags);
+ dest.writeTypedObject(mIonosphericModel, flags);
+ dest.writeTypedObject(mUtcModel, flags);
+ dest.writeTypedObject(mLeapSecondsModel, flags);
+ dest.writeTypedList(mTimeModels);
+ dest.writeTypedList(mSatelliteEphemeris);
+ dest.writeTypedList(mRealTimeIntegrityModels);
+ dest.writeTypedList(mSatelliteCorrections);
+ }
+
+ public static final @android.annotation.NonNull Creator<BeidouAssistance> CREATOR =
+ new Creator<BeidouAssistance>() {
+ @Override
+ public BeidouAssistance createFromParcel(Parcel in) {
+ return new BeidouAssistance.Builder()
+ .setAlmanac(in.readTypedObject(GnssAlmanac.CREATOR))
+ .setIonosphericModel(
+ in.readTypedObject(KlobucharIonosphericModel.CREATOR))
+ .setUtcModel(in.readTypedObject(UtcModel.CREATOR))
+ .setLeapSecondsModel(in.readTypedObject(LeapSecondsModel.CREATOR))
+ .setTimeModels(in.createTypedArrayList(TimeModel.CREATOR))
+ .setSatelliteEphemeris(
+ in.createTypedArrayList(BeidouSatelliteEphemeris.CREATOR))
+ .setRealTimeIntegrityModels(
+ in.createTypedArrayList(RealTimeIntegrityModel.CREATOR))
+ .setSatelliteCorrections(
+ in.createTypedArrayList(GnssSatelliteCorrections.CREATOR))
+ .build();
+ }
+
+ @Override
+ public BeidouAssistance[] newArray(int size) {
+ return new BeidouAssistance[size];
+ }
+ };
+
+ /** Builder for {@link BeidouAssistance}. */
+ public static final class Builder {
+ private GnssAlmanac mAlmanac;
+ private KlobucharIonosphericModel mIonosphericModel;
+ private UtcModel mUtcModel;
+ private LeapSecondsModel mLeapSecondsModel;
+ private List<TimeModel> mTimeModels;
+ private List<BeidouSatelliteEphemeris> mSatelliteEphemeris;
+ private List<RealTimeIntegrityModel> mRealTimeIntegrityModels;
+ private List<GnssSatelliteCorrections> mSatelliteCorrections;
+
+ /** Sets the Beidou almanac. */
+ @NonNull
+ public Builder setAlmanac(@Nullable GnssAlmanac almanac) {
+ mAlmanac = almanac;
+ return this;
+ }
+
+ /** Sets the Klobuchar ionospheric model. */
+ @NonNull
+ public Builder setIonosphericModel(@Nullable KlobucharIonosphericModel ionosphericModel) {
+ mIonosphericModel = ionosphericModel;
+ return this;
+ }
+
+ /** Sets the UTC model. */
+ @NonNull
+ public Builder setUtcModel(@Nullable UtcModel utcModel) {
+ mUtcModel = utcModel;
+ return this;
+ }
+
+ /** Sets the leap seconds model. */
+ @NonNull
+ public Builder setLeapSecondsModel(@Nullable LeapSecondsModel leapSecondsModel) {
+ mLeapSecondsModel = leapSecondsModel;
+ return this;
+ }
+
+ /** Sets the list of time models. */
+ @NonNull
+ public Builder setTimeModels(
+ @Nullable @SuppressLint("NullableCollection") List<TimeModel> timeModels) {
+ mTimeModels = timeModels;
+ return this;
+ }
+
+ /** Sets the list of Beidou ephemeris. */
+ @NonNull
+ public Builder setSatelliteEphemeris(
+ @Nullable @SuppressLint("NullableCollection")
+ List<BeidouSatelliteEphemeris> satelliteEphemeris) {
+ mSatelliteEphemeris = satelliteEphemeris;
+ return this;
+ }
+
+ /** Sets the list of real time integrity models. */
+ @NonNull
+ public Builder setRealTimeIntegrityModels(
+ @Nullable @SuppressLint("NullableCollection")
+ List<RealTimeIntegrityModel> realTimeIntegrityModels) {
+ mRealTimeIntegrityModels = realTimeIntegrityModels;
+ return this;
+ }
+
+ /** Sets the list of Beidou satellite corrections. */
+ @NonNull
+ public Builder setSatelliteCorrections(
+ @Nullable @SuppressLint("NullableCollection")
+ List<GnssSatelliteCorrections> satelliteCorrections) {
+ mSatelliteCorrections = satelliteCorrections;
+ return this;
+ }
+
+ /** Builds the {@link BeidouAssistance}. */
+ @NonNull
+ public BeidouAssistance build() {
+ return new BeidouAssistance(this);
+ }
+ }
+}
diff --git a/location/java/android/location/BeidouSatelliteEphemeris.java b/location/java/android/location/BeidouSatelliteEphemeris.java
new file mode 100644
index 0000000..6bd91e3
--- /dev/null
+++ b/location/java/android/location/BeidouSatelliteEphemeris.java
@@ -0,0 +1,647 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class contains ephemeris parameters specific to Beidou satellites.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class BeidouSatelliteEphemeris implements Parcelable {
+ /** The PRN number of the Beidou satellite. */
+ private final int mPrn;
+
+ /** Satellite clock model. */
+ private final BeidouSatelliteClockModel mSatelliteClockModel;
+
+ /** Satellite orbit model. */
+ private final KeplerianOrbitModel mSatelliteOrbitModel;
+
+ /** Satellite health. */
+ private final BeidouSatelliteHealth mSatelliteHealth;
+
+ /** Satellite ephemeris time. */
+ private final BeidouSatelliteEphemerisTime mSatelliteEphemerisTime;
+
+ private BeidouSatelliteEphemeris(Builder builder) {
+ // Allow PRN beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mPrn >= 1);
+ Preconditions.checkNotNull(builder.mSatelliteClockModel,
+ "SatelliteClockModel cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteOrbitModel,
+ "SatelliteOrbitModel cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteHealth,
+ "SatelliteHealth cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteEphemerisTime,
+ "SatelliteEphemerisTime cannot be null");
+ mPrn = builder.mPrn;
+ mSatelliteClockModel = builder.mSatelliteClockModel;
+ mSatelliteOrbitModel = builder.mSatelliteOrbitModel;
+ mSatelliteHealth = builder.mSatelliteHealth;
+ mSatelliteEphemerisTime = builder.mSatelliteEphemerisTime;
+ }
+
+ /** Returns the PRN of the satellite. */
+ @IntRange(from = 1, to = 63)
+ public int getPrn() {
+ return mPrn;
+ }
+
+ /** Returns the satellite clock model. */
+ @NonNull
+ public BeidouSatelliteClockModel getSatelliteClockModel() {
+ return mSatelliteClockModel;
+ }
+
+ /** Returns the satellite orbit model. */
+ @NonNull
+ public KeplerianOrbitModel getSatelliteOrbitModel() {
+ return mSatelliteOrbitModel;
+ }
+
+ /** Returns the satellite health. */
+ @NonNull
+ public BeidouSatelliteHealth getSatelliteHealth() {
+ return mSatelliteHealth;
+ }
+
+ /** Returns the satellite ephemeris time. */
+ @NonNull
+ public BeidouSatelliteEphemerisTime getSatelliteEphemerisTime() {
+ return mSatelliteEphemerisTime;
+ }
+
+ public static final @NonNull Creator<BeidouSatelliteEphemeris> CREATOR =
+ new Creator<BeidouSatelliteEphemeris>() {
+ @Override
+ @NonNull
+ public BeidouSatelliteEphemeris createFromParcel(Parcel in) {
+ final BeidouSatelliteEphemeris.Builder beidouSatelliteEphemeris =
+ new Builder()
+ .setPrn(in.readInt())
+ .setSatelliteClockModel(
+ in.readTypedObject(BeidouSatelliteClockModel.CREATOR))
+ .setSatelliteOrbitModel(
+ in.readTypedObject(KeplerianOrbitModel.CREATOR))
+ .setSatelliteHealth(
+ in.readTypedObject(BeidouSatelliteHealth.CREATOR))
+ .setSatelliteEphemerisTime(
+ in.readTypedObject(
+ BeidouSatelliteEphemerisTime.CREATOR));
+ return beidouSatelliteEphemeris.build();
+ }
+
+ @Override
+ public BeidouSatelliteEphemeris[] newArray(int size) {
+ return new BeidouSatelliteEphemeris[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mPrn);
+ parcel.writeTypedObject(mSatelliteClockModel, flags);
+ parcel.writeTypedObject(mSatelliteOrbitModel, flags);
+ parcel.writeTypedObject(mSatelliteHealth, flags);
+ parcel.writeTypedObject(mSatelliteEphemerisTime, flags);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("BeidouSatelliteEphemeris[");
+ builder.append("prn = ").append(mPrn);
+ builder.append(", satelliteClockModel = ").append(mSatelliteClockModel);
+ builder.append(", satelliteOrbitModel = ").append(mSatelliteOrbitModel);
+ builder.append(", satelliteHealth = ").append(mSatelliteHealth);
+ builder.append(", satelliteEphemerisTime = ").append(mSatelliteEphemerisTime);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link BeidouSatelliteEphemeris} */
+ public static final class Builder {
+ private int mPrn;
+ private BeidouSatelliteClockModel mSatelliteClockModel;
+ private KeplerianOrbitModel mSatelliteOrbitModel;
+ private BeidouSatelliteHealth mSatelliteHealth;
+ private BeidouSatelliteEphemerisTime mSatelliteEphemerisTime;
+
+ /** Sets the PRN of the satellite. */
+ @NonNull
+ public Builder setPrn(int prn) {
+ mPrn = prn;
+ return this;
+ }
+
+ /** Sets the satellite clock model. */
+ @NonNull
+ public Builder setSatelliteClockModel(
+ @NonNull BeidouSatelliteClockModel satelliteClockModel) {
+ mSatelliteClockModel = satelliteClockModel;
+ return this;
+ }
+
+ /** Sets the satellite orbit model. */
+ @NonNull
+ public Builder setSatelliteOrbitModel(@NonNull KeplerianOrbitModel satelliteOrbitModel) {
+ mSatelliteOrbitModel = satelliteOrbitModel;
+ return this;
+ }
+
+ /** Sets the satellite health. */
+ @NonNull
+ public Builder setSatelliteHealth(@NonNull BeidouSatelliteHealth satelliteHealth) {
+ mSatelliteHealth = satelliteHealth;
+ return this;
+ }
+
+ /** Sets the satellite ephemeris time. */
+ @NonNull
+ public Builder setSatelliteEphemerisTime(
+ @NonNull BeidouSatelliteEphemerisTime satelliteEphemerisTime) {
+ mSatelliteEphemerisTime = satelliteEphemerisTime;
+ return this;
+ }
+
+ /** Builds a {@link BeidouSatelliteEphemeris} instance as specified by this builder. */
+ @NonNull
+ public BeidouSatelliteEphemeris build() {
+ return new BeidouSatelliteEphemeris(this);
+ }
+ }
+
+ /**
+ * A class contains the set of parameters needed for Beidou satellite clock correction.
+ *
+ * <p>This is defined in BDS-SIS-ICD-B1I-3.0, section 5.2.4.9, 5.2.4.10.
+ */
+ public static final class BeidouSatelliteClockModel implements Parcelable {
+ /**
+ * Time of the clock in seconds since Beidou epoch.
+ *
+ * <p>Corresponds to the 'Epoch' field within the 'SV/EPOCH/SV CLK' record of Beidou
+ * navigation message in RINEX 3.05 Table A14.
+ */
+ private final long mTimeOfClockSeconds;
+
+ /** SV clock bias in seconds. */
+ private final double mAf0;
+
+ /** SV clock drift in seconds per second. */
+ private final double mAf1;
+
+ /** SV clock drift in seconds per second squared. */
+ private final double mAf2;
+
+ /** Group delay differential 1 B1/B3 in seconds. */
+ private final double mTgd1;
+
+ /** Group delay differential 2 B2/B3 in seconds. */
+ private final double mTgd2;
+
+ /**
+ * Age of Data Clock.
+ * <p>This is defined in BDS-SIS-ICD-B1I-3.0 Section 5.2.4.8 Table 5-6.
+ */
+ private final int mAodc;
+
+ private BeidouSatelliteClockModel(Builder builder) {
+ Preconditions.checkArgument(builder.mTimeOfClockSeconds >= 0);
+ Preconditions.checkArgumentInRange(builder.mAf0, -9.77e-3f, 9.77e-3f, "Af0");
+ Preconditions.checkArgumentInRange(builder.mAf1, -1.87e-9f, 1.87e-9f, "Af1");
+ Preconditions.checkArgumentInRange(builder.mAf2, -1.39e-17f, 1.39e-17f, "Af2");
+ Preconditions.checkArgumentInRange(builder.mTgd1, -5.12e-8f, 5.12e-8f, "Tgd1");
+ Preconditions.checkArgumentInRange(builder.mTgd2, -5.12e-8f, 5.12e-8f, "Tgd2");
+ Preconditions.checkArgumentInRange(builder.mAodc, 0, 31, "Aodc");
+ mTimeOfClockSeconds = builder.mTimeOfClockSeconds;
+ mAf0 = builder.mAf0;
+ mAf1 = builder.mAf1;
+ mAf2 = builder.mAf2;
+ mTgd1 = builder.mTgd1;
+ mTgd2 = builder.mTgd2;
+ mAodc = builder.mAodc;
+ }
+
+ /** Returns the time of the clock in seconds since Beidou epoch. */
+ @IntRange(from = 0)
+ public long getTimeOfClockSeconds() {
+ return mTimeOfClockSeconds;
+ }
+
+ /** Returns the SV clock bias in seconds. */
+ @FloatRange(from = -9.77e-3f, to = 9.77e-3f)
+ public double getAf0() {
+ return mAf0;
+ }
+
+ /** Returns the SV clock drift in seconds per second. */
+ @FloatRange(from = -1.87e-9f, to = 1.87e-9f)
+ public double getAf1() {
+ return mAf1;
+ }
+
+ /** Returns the SV clock drift in seconds per second squared. */
+ @FloatRange(from = -1.39e-17f, to = 1.39e-17f)
+ public double getAf2() {
+ return mAf2;
+ }
+
+ /** Returns the group delay differential 1 B1/B3 in seconds. */
+ @FloatRange(from = -5.12e-8f, to = 5.12e-8f)
+ public double getTgd1() {
+ return mTgd1;
+ }
+
+ /** Returns the group delay differential 2 B2/B3 in seconds. */
+ @FloatRange(from = -5.12e-8f, to = 5.12e-8f)
+ public double getTgd2() {
+ return mTgd2;
+ }
+
+ /** Returns the age of data clock. */
+ @IntRange(from = 0, to = 31)
+ public int getAodc() {
+ return mAodc;
+ }
+
+ public static final @NonNull Creator<BeidouSatelliteClockModel> CREATOR =
+ new Creator<BeidouSatelliteClockModel>() {
+ @Override
+ @NonNull
+ public BeidouSatelliteClockModel createFromParcel(Parcel in) {
+ final BeidouSatelliteClockModel.Builder beidouSatelliteClockModel =
+ new Builder()
+ .setTimeOfClockSeconds(in.readLong())
+ .setAf0(in.readDouble())
+ .setAf1(in.readDouble())
+ .setAf2(in.readDouble())
+ .setTgd1(in.readDouble())
+ .setTgd2(in.readDouble())
+ .setAodc(in.readInt());
+ return beidouSatelliteClockModel.build();
+ }
+
+ @Override
+ public BeidouSatelliteClockModel[] newArray(int size) {
+ return new BeidouSatelliteClockModel[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeLong(mTimeOfClockSeconds);
+ parcel.writeDouble(mAf0);
+ parcel.writeDouble(mAf1);
+ parcel.writeDouble(mAf2);
+ parcel.writeDouble(mTgd1);
+ parcel.writeDouble(mTgd2);
+ parcel.writeInt(mAodc);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("BeidouSatelliteClockModel[");
+ builder.append("timeOfClockSeonds = ").append(mTimeOfClockSeconds);
+ builder.append(", af0 = ").append(mAf0);
+ builder.append(", af1 = ").append(mAf1);
+ builder.append(", af2 = ").append(mAf2);
+ builder.append(", tgd1 = ").append(mTgd1);
+ builder.append(", tgd2 = ").append(mTgd2);
+ builder.append(", aodc = ").append(mAodc);
+ return builder.toString();
+ }
+
+ /** Builder for {@link BeidouSatelliteClockModel} */
+ public static final class Builder {
+ private long mTimeOfClockSeconds;
+ private double mAf0;
+ private double mAf1;
+ private double mAf2;
+ private double mTgd1;
+ private double mTgd2;
+ private int mAodc;
+
+ /** Sets the time of the clock in seconds since Beidou epoch. */
+ @NonNull
+ public Builder setTimeOfClockSeconds(@IntRange(from = 0) long timeOfClockSeconds) {
+ mTimeOfClockSeconds = timeOfClockSeconds;
+ return this;
+ }
+
+ /** Sets the SV clock bias in seconds. */
+ @NonNull
+ public Builder setAf0(@FloatRange(from = -9.77e-3f, to = 9.77e-3f)double af0) {
+ mAf0 = af0;
+ return this;
+ }
+
+ /** Sets the SV clock drift in seconds per second. */
+ @NonNull
+ public Builder setAf1(@FloatRange(from = -1.87e-9f, to = 1.87e-9f) double af1) {
+ mAf1 = af1;
+ return this;
+ }
+
+ /** Sets the SV clock drift in seconds per second squared. */
+ @NonNull
+ public Builder setAf2(@FloatRange(from = -1.39e-17f, to = 1.39e-17f) double af2) {
+ mAf2 = af2;
+ return this;
+ }
+
+ /** Sets the group delay differential 1 B1/B3 in seconds. */
+ @NonNull
+ public Builder setTgd1(@FloatRange(from = -5.12e-8f, to = 5.12e-8f) double tgd1) {
+ mTgd1 = tgd1;
+ return this;
+ }
+
+ /** Sets the group delay differential 2 B2/B3 in seconds. */
+ @NonNull
+ public Builder setTgd2(@FloatRange(from = -5.12e-8f, to = 5.12e-8f) double tgd2) {
+ mTgd2 = tgd2;
+ return this;
+ }
+
+ /** Sets the age of data clock. */
+ @NonNull
+ public Builder setAodc(@IntRange(from = 0, to = 31) int aodc) {
+ mAodc = aodc;
+ return this;
+ }
+
+ /** Builds a {@link BeidouSatelliteClockModel} instance as specified by this builder. */
+ @NonNull
+ public BeidouSatelliteClockModel build() {
+ return new BeidouSatelliteClockModel(this);
+ }
+ }
+ }
+
+ /** A class contains Beidou satellite health. */
+ public static final class BeidouSatelliteHealth implements Parcelable {
+ /**
+ * The autonomous satellite health flag (SatH1) occupies 1 bit.
+ *
+ * <p>“0” means broadcasting satellite is good and “1” means not.
+ *
+ * <p>This is defined in BDS-SIS-ICD-B1I-3.0 section 5.2.4.6.
+ */
+ private final int mSatH1;
+
+ /**
+ * SV accuracy in meters.
+ *
+ * <p>This is defined in the "BROADCAST ORBIT - 6" record of RINEX 3.05
+ * Table A14, pp.78.
+ */
+ private final double mSvAccur;
+
+ private BeidouSatelliteHealth(Builder builder) {
+ // Allow SatH1 beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mSatH1 >= 0);
+ Preconditions.checkArgumentInRange(builder.mSvAccur, 0.0f, 8192.0f, "SvAccur");
+ mSatH1 = builder.mSatH1;
+ mSvAccur = builder.mSvAccur;
+ }
+
+ /** Returns the autonomous satellite health flag (SatH1) */
+ @IntRange(from = 0, to = 1)
+ public int getSatH1() {
+ return mSatH1;
+ }
+
+ /** Returns the SV accuracy in meters. */
+ @FloatRange(from = 0.0f, to = 8192.0f)
+ public double getSvAccur() {
+ return mSvAccur;
+ }
+
+ public static final @NonNull Creator<BeidouSatelliteHealth> CREATOR =
+ new Creator<BeidouSatelliteHealth>() {
+ @Override
+ @NonNull
+ public BeidouSatelliteHealth createFromParcel(Parcel in) {
+ final BeidouSatelliteHealth.Builder beidouSatelliteHealth =
+ new Builder().setSatH1(in.readInt()).setSvAccur(in.readDouble());
+ return beidouSatelliteHealth.build();
+ }
+
+ @Override
+ public BeidouSatelliteHealth[] newArray(int size) {
+ return new BeidouSatelliteHealth[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mSatH1);
+ parcel.writeDouble(mSvAccur);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("BeidouSatelliteHealth[");
+ builder.append("satH1 = ").append(mSatH1);
+ builder.append(", svAccur = ").append(mSvAccur);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link BeidouSatelliteHealth} */
+ public static final class Builder {
+ private int mSatH1;
+ private double mSvAccur;
+
+ /** Sets the autonomous satellite health flag (SatH1) */
+ @NonNull
+ public Builder setSatH1(int satH1) {
+ mSatH1 = satH1;
+ return this;
+ }
+
+ /** Sets the SV accuracy in meters. */
+ @NonNull
+ public Builder setSvAccur(double svAccur) {
+ mSvAccur = svAccur;
+ return this;
+ }
+
+ /** Builds a {@link BeidouSatelliteHealth} instance as specified by this builder. */
+ @NonNull
+ public BeidouSatelliteHealth build() {
+ return new BeidouSatelliteHealth(this);
+ }
+ }
+ }
+
+ /** A class contains Beidou satellite ephemeris time. */
+ public static final class BeidouSatelliteEphemerisTime implements Parcelable {
+ /**
+ * AODE Age of Data, Ephemeris.
+ *
+ * <p>This is defined in BDS-SIS-ICD-B1I-3.0 section 5.2.4.11 Table 5-8.
+ */
+ private final int mIode;
+
+ /** Beidou week number without rollover */
+ private final int mBeidouWeekNumber;
+
+ /**
+ * Time of ephemeris in seconds.
+ *
+ * <p>This is defined in BDS-SIS-ICD-B1I-3.0 section 5.2.4.12.
+ */
+ private final int mToeSeconds;
+
+ private BeidouSatelliteEphemerisTime(Builder builder) {
+ Preconditions.checkArgumentInRange(builder.mIode, 0, 31, "Iode");
+ Preconditions.checkArgument(builder.mBeidouWeekNumber >= 0);
+ Preconditions.checkArgumentInRange(builder.mToeSeconds, 0, 604792, "ToeSeconds");
+ mIode = builder.mIode;
+ mBeidouWeekNumber = builder.mBeidouWeekNumber;
+ mToeSeconds = builder.mToeSeconds;
+ }
+
+ /** Returns the AODE Age of Data, Ephemeris. */
+ @IntRange(from = 0, to = 31)
+ public int getIode() {
+ return mIode;
+ }
+
+ /** Returns the Beidou week number without rollover . */
+ @IntRange(from = 0)
+ public int getBeidouWeekNumber() {
+ return mBeidouWeekNumber;
+ }
+
+ /** Returns the time of ephemeris in seconds. */
+ @IntRange(from = 0, to = 604792)
+ public int getToeSeconds() {
+ return mToeSeconds;
+ }
+
+ public static final @NonNull Creator<BeidouSatelliteEphemerisTime> CREATOR =
+ new Creator<BeidouSatelliteEphemerisTime>() {
+ @Override
+ @NonNull
+ public BeidouSatelliteEphemerisTime createFromParcel(Parcel in) {
+ final BeidouSatelliteEphemerisTime.Builder beidouSatelliteEphemerisTime =
+ new Builder()
+ .setIode(in.readInt())
+ .setBeidouWeekNumber(in.readInt())
+ .setToeSeconds(in.readInt());
+ return beidouSatelliteEphemerisTime.build();
+ }
+
+ @Override
+ public BeidouSatelliteEphemerisTime[] newArray(int size) {
+ return new BeidouSatelliteEphemerisTime[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mIode);
+ parcel.writeInt(mBeidouWeekNumber);
+ parcel.writeInt(mToeSeconds);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("BeidouSatelliteEphemerisTime[");
+ builder.append("iode = ").append(mIode);
+ builder.append(", beidouWeekNumber = ").append(mBeidouWeekNumber);
+ builder.append(", toeSeconds = ").append(mToeSeconds);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link BeidouSatelliteEphemerisTime} */
+ public static final class Builder {
+ private int mIode;
+ private int mBeidouWeekNumber;
+ private int mToeSeconds;
+
+ /** Sets the AODE Age of Data, Ephemeris. */
+ @NonNull
+ public Builder setIode(int iode) {
+ mIode = iode;
+ return this;
+ }
+
+ /** Sets the Beidou week number without rollover */
+ @NonNull
+ public Builder setBeidouWeekNumber(
+ @IntRange(from = 0) int beidouWeekNumber) {
+ mBeidouWeekNumber = beidouWeekNumber;
+ return this;
+ }
+
+ /** Sets the time of ephemeris in seconds. */
+ @NonNull
+ public Builder setToeSeconds(@IntRange(from = 0, to = 604792) int toeSeconds) {
+ mToeSeconds = toeSeconds;
+ return this;
+ }
+
+ /**
+ * Builds a {@link BeidouSatelliteEphemerisTime} instance as specified by this builder.
+ */
+ @NonNull
+ public BeidouSatelliteEphemerisTime build() {
+ return new BeidouSatelliteEphemerisTime(this);
+ }
+ }
+ }
+}
diff --git a/location/java/android/location/GalileoAssistance.java b/location/java/android/location/GalileoAssistance.java
new file mode 100644
index 0000000..07c5bab
--- /dev/null
+++ b/location/java/android/location/GalileoAssistance.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.location.GnssAssistance.GnssSatelliteCorrections;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class contains Galileo assistance.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GalileoAssistance implements Parcelable {
+
+ /** The Galileo almanac. */
+ @Nullable private final GnssAlmanac mAlmanac;
+
+ /** The Klobuchar ionospheric model. */
+ @Nullable private final KlobucharIonosphericModel mIonosphericModel;
+
+ /** The UTC model. */
+ @Nullable private final UtcModel mUtcModel;
+
+ /** The leap seconds model. */
+ @Nullable private final LeapSecondsModel mLeapSecondsModel;
+
+ /** The list of time models. */
+ @NonNull private final List<TimeModel> mTimeModels;
+
+ /** The list of Galileo ephemeris. */
+ @NonNull private final List<GalileoSatelliteEphemeris> mSatelliteEphemeris;
+
+ /** The list of real time integrity models. */
+ @NonNull private final List<RealTimeIntegrityModel> mRealTimeIntegrityModels;
+
+ /** The list of Galileo satellite corrections. */
+ @NonNull private final List<GnssSatelliteCorrections> mSatelliteCorrections;
+
+ private GalileoAssistance(Builder builder) {
+ mAlmanac = builder.mAlmanac;
+ mIonosphericModel = builder.mIonosphericModel;
+ mUtcModel = builder.mUtcModel;
+ mLeapSecondsModel = builder.mLeapSecondsModel;
+ if (builder.mTimeModels != null) {
+ mTimeModels = Collections.unmodifiableList(new ArrayList<>(builder.mTimeModels));
+ } else {
+ mTimeModels = new ArrayList<>();
+ }
+ if (builder.mSatelliteEphemeris != null) {
+ mSatelliteEphemeris =
+ Collections.unmodifiableList(new ArrayList<>(builder.mSatelliteEphemeris));
+ } else {
+ mSatelliteEphemeris = new ArrayList<>();
+ }
+ if (builder.mRealTimeIntegrityModels != null) {
+ mRealTimeIntegrityModels =
+ Collections.unmodifiableList(new ArrayList<>(builder.mRealTimeIntegrityModels));
+ } else {
+ mRealTimeIntegrityModels = new ArrayList<>();
+ }
+ if (builder.mSatelliteCorrections != null) {
+ mSatelliteCorrections =
+ Collections.unmodifiableList(new ArrayList<>(builder.mSatelliteCorrections));
+ } else {
+ mSatelliteCorrections = new ArrayList<>();
+ }
+ }
+
+ /** Returns the Galileo almanac. */
+ @Nullable
+ public GnssAlmanac getAlmanac() {
+ return mAlmanac;
+ }
+
+ /** Returns the Klobuchar ionospheric model. */
+ @Nullable
+ public KlobucharIonosphericModel getIonosphericModel() {
+ return mIonosphericModel;
+ }
+
+ /** Returns the UTC model. */
+ @Nullable
+ public UtcModel getUtcModel() {
+ return mUtcModel;
+ }
+
+ /** Returns the leap seconds model. */
+ @Nullable
+ public LeapSecondsModel getLeapSecondsModel() {
+ return mLeapSecondsModel;
+ }
+
+ /** Returns the list of time models. */
+ @NonNull
+ public List<TimeModel> getTimeModels() {
+ return mTimeModels;
+ }
+
+ /** Returns the list of Galileo ephemeris. */
+ @NonNull
+ public List<GalileoSatelliteEphemeris> getSatelliteEphemeris() {
+ return mSatelliteEphemeris;
+ }
+
+ /** Returns the list of real time integrity models. */
+ @NonNull
+ public List<RealTimeIntegrityModel> getRealTimeIntegrityModels() {
+ return mRealTimeIntegrityModels;
+ }
+
+ /** Returns the list of Galileo satellite corrections. */
+ @NonNull
+ public List<GnssSatelliteCorrections> getSatelliteCorrections() {
+ return mSatelliteCorrections;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(mAlmanac, flags);
+ dest.writeTypedObject(mIonosphericModel, flags);
+ dest.writeTypedObject(mUtcModel, flags);
+ dest.writeTypedObject(mLeapSecondsModel, flags);
+ dest.writeTypedList(mTimeModels);
+ dest.writeTypedList(mSatelliteEphemeris);
+ dest.writeTypedList(mRealTimeIntegrityModels);
+ dest.writeTypedList(mSatelliteCorrections);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GalileoAssistance[");
+ builder.append("almanac = ").append(mAlmanac);
+ builder.append(", ionosphericModel = ").append(mIonosphericModel);
+ builder.append(", utcModel = ").append(mUtcModel);
+ builder.append(", leapSecondsModel = ").append(mLeapSecondsModel);
+ builder.append(", timeModels = ").append(mTimeModels);
+ builder.append(", satelliteEphemeris = ").append(mSatelliteEphemeris);
+ builder.append(", realTimeIntegrityModels = ").append(mRealTimeIntegrityModels);
+ builder.append(", satelliteCorrections = ").append(mSatelliteCorrections);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ public static final @android.annotation.NonNull Creator<GalileoAssistance> CREATOR =
+ new Creator<GalileoAssistance>() {
+ @Override
+ public GalileoAssistance createFromParcel(Parcel in) {
+ return new GalileoAssistance.Builder()
+ .setAlmanac(in.readTypedObject(GnssAlmanac.CREATOR))
+ .setIonosphericModel(
+ in.readTypedObject(KlobucharIonosphericModel.CREATOR))
+ .setUtcModel(in.readTypedObject(UtcModel.CREATOR))
+ .setLeapSecondsModel(in.readTypedObject(LeapSecondsModel.CREATOR))
+ .setTimeModels(in.createTypedArrayList(TimeModel.CREATOR))
+ .setSatelliteEphemeris(
+ in.createTypedArrayList(GalileoSatelliteEphemeris.CREATOR))
+ .setRealTimeIntegrityModels(
+ in.createTypedArrayList(RealTimeIntegrityModel.CREATOR))
+ .setSatelliteCorrections(
+ in.createTypedArrayList(GnssSatelliteCorrections.CREATOR))
+ .build();
+ }
+
+ @Override
+ public GalileoAssistance[] newArray(int size) {
+ return new GalileoAssistance[size];
+ }
+ };
+
+ /** Builder for {@link GalileoAssistance}. */
+ public static final class Builder {
+ private GnssAlmanac mAlmanac;
+ private KlobucharIonosphericModel mIonosphericModel;
+ private UtcModel mUtcModel;
+ private LeapSecondsModel mLeapSecondsModel;
+ private List<TimeModel> mTimeModels;
+ private List<GalileoSatelliteEphemeris> mSatelliteEphemeris;
+ private List<RealTimeIntegrityModel> mRealTimeIntegrityModels;
+ private List<GnssSatelliteCorrections> mSatelliteCorrections;
+
+ /** Sets the Galileo almanac. */
+ @NonNull
+ public Builder setAlmanac(@Nullable GnssAlmanac almanac) {
+ mAlmanac = almanac;
+ return this;
+ }
+
+ /** Sets the Klobuchar ionospheric model. */
+ @NonNull
+ public Builder setIonosphericModel(@Nullable KlobucharIonosphericModel ionosphericModel) {
+ mIonosphericModel = ionosphericModel;
+ return this;
+ }
+
+ /** Sets the UTC model. */
+ @NonNull
+ public Builder setUtcModel(@Nullable UtcModel utcModel) {
+ mUtcModel = utcModel;
+ return this;
+ }
+
+ /** Sets the leap seconds model. */
+ @NonNull
+ public Builder setLeapSecondsModel(@Nullable LeapSecondsModel leapSecondsModel) {
+ mLeapSecondsModel = leapSecondsModel;
+ return this;
+ }
+
+ /** Sets the list of time models. */
+ @NonNull
+ public Builder setTimeModels(
+ @Nullable @SuppressLint("NullableCollection") List<TimeModel> timeModels) {
+ mTimeModels = timeModels;
+ return this;
+ }
+
+ /** Sets the list of Galileo ephemeris. */
+ @NonNull
+ public Builder setSatelliteEphemeris(
+ @Nullable @SuppressLint("NullableCollection")
+ List<GalileoSatelliteEphemeris> satelliteEphemeris) {
+ mSatelliteEphemeris = satelliteEphemeris;
+ return this;
+ }
+
+ /** Sets the list of real time integrity models. */
+ @NonNull
+ public Builder setRealTimeIntegrityModels(
+ @Nullable @SuppressLint("NullableCollection")
+ List<RealTimeIntegrityModel> realTimeIntegrityModels) {
+ mRealTimeIntegrityModels = realTimeIntegrityModels;
+ return this;
+ }
+
+ /** Sets the list of Galileo satellite corrections. */
+ @NonNull
+ public Builder setSatelliteCorrections(
+ @Nullable @SuppressLint("NullableCollection")
+ List<GnssSatelliteCorrections> satelliteCorrections) {
+ mSatelliteCorrections = satelliteCorrections;
+ return this;
+ }
+
+ /** Builds the {@link GalileoAssistance}. */
+ @NonNull
+ public GalileoAssistance build() {
+ return new GalileoAssistance(this);
+ }
+ }
+}
diff --git a/location/java/android/location/GalileoIonosphericModel.java b/location/java/android/location/GalileoIonosphericModel.java
new file mode 100644
index 0000000..6638be9
--- /dev/null
+++ b/location/java/android/location/GalileoIonosphericModel.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Contains Galileo ionospheric model.
+ *
+ * <p>This is defined in Galileo-OS-SIS-ICD-v2.1, section 5.1.6.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GalileoIonosphericModel implements Parcelable {
+ /** Effective ionisation level 1st order parameter in sfu. */
+ private final double mAi0;
+
+ /** Effective ionisation level 2nd order parameter in sfu per degree. */
+ private final double mAi1;
+
+ /** Effective ionisation level 3nd order parameter in sfu per degree squared. */
+ private final double mAi2;
+
+ private GalileoIonosphericModel(Builder builder) {
+ Preconditions.checkArgumentInRange(builder.mAi0, 0.0f, 512.0f, "Ai0");
+ Preconditions.checkArgumentInRange(builder.mAi1, -4.0f, 4.0f, "Ai1");
+ Preconditions.checkArgumentInRange(builder.mAi2, -0.5f, 0.5f, "Ai2");
+ mAi0 = builder.mAi0;
+ mAi1 = builder.mAi1;
+ mAi2 = builder.mAi2;
+ }
+
+ /** Returns the effective ionisation level 1st order parameter in sfu. */
+ @FloatRange(from = 0.0f, to = 512.0f)
+ public double getAi0() {
+ return mAi0;
+ }
+
+ /** Returns the effective ionisation level 2nd order parameter in sfu per degree. */
+ @FloatRange(from = -4.0f, to = 4.0f)
+ public double getAi1() {
+ return mAi1;
+ }
+
+ /** Returns the effective ionisation level 3nd order parameter in sfu per degree squared. */
+ @FloatRange(from = -0.5f, to = 0.5f)
+ public double getAi2() {
+ return mAi2;
+ }
+
+ public static final @NonNull Parcelable.Creator<GalileoIonosphericModel> CREATOR =
+ new Parcelable.Creator<GalileoIonosphericModel>() {
+ @Override
+ public GalileoIonosphericModel createFromParcel(@NonNull Parcel source) {
+ return new GalileoIonosphericModel.Builder()
+ .setAi0(source.readDouble())
+ .setAi1(source.readDouble())
+ .setAi2(source.readDouble())
+ .build();
+ }
+
+ @Override
+ public GalileoIonosphericModel[] newArray(int size) {
+ return new GalileoIonosphericModel[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeDouble(mAi0);
+ dest.writeDouble(mAi1);
+ dest.writeDouble(mAi2);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GalileoIonosphericModel[");
+ builder.append("ai0 = ").append(mAi0);
+ builder.append(", ai1 = ").append(mAi1);
+ builder.append(", ai2 = ").append(mAi2);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GalileoIonosphericModel}. */
+ public static final class Builder {
+ private double mAi0;
+ private double mAi1;
+ private double mAi2;
+
+ /** Sets the effective ionisation level 1st order parameter in sfu. */
+ @NonNull
+ public Builder setAi0(@FloatRange(from = 0.0f, to = 512.0f) double ai0) {
+ mAi0 = ai0;
+ return this;
+ }
+
+ /** Sets the effective ionisation level 2nd order parameter in sfu per degree. */
+ @NonNull
+ public Builder setAi1(@FloatRange(from = -4.0f, to = 4.0f) double ai1) {
+ mAi1 = ai1;
+ return this;
+ }
+
+ /** Sets the effective ionisation level 3nd order parameter in sfu per degree squared. */
+ @NonNull
+ public Builder setAi2(@FloatRange(from = -0.5f, to = 0.5f) double ai2) {
+ mAi2 = ai2;
+ return this;
+ }
+
+ /** Builds a {@link GalileoIonosphericModel}. */
+ @NonNull
+ public GalileoIonosphericModel build() {
+ return new GalileoIonosphericModel(this);
+ }
+ }
+}
diff --git a/location/java/android/location/GalileoSatelliteEphemeris.java b/location/java/android/location/GalileoSatelliteEphemeris.java
new file mode 100644
index 0000000..7dd3711
--- /dev/null
+++ b/location/java/android/location/GalileoSatelliteEphemeris.java
@@ -0,0 +1,660 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Contains ephemeris parameters specific to Galileo satellites.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GalileoSatelliteEphemeris implements Parcelable {
+
+ /** Satellite code number. */
+ private int mSatelliteCodeNumber;
+
+ /** Array of satellite clock model. */
+ @NonNull private final List<GalileoSatelliteClockModel> mSatelliteClockModels;
+
+ /** Satellite orbit model. */
+ @NonNull private final KeplerianOrbitModel mSatelliteOrbitModel;
+
+ /** Satellite health. */
+ @NonNull private final GalileoSvHealth mSatelliteHealth;
+
+ /** Satellite ephemeris time. */
+ @NonNull private final SatelliteEphemerisTime mSatelliteEphemerisTime;
+
+ private GalileoSatelliteEphemeris(Builder builder) {
+ // Allow satelliteCodeNumber beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mSatelliteCodeNumber >= 1);
+ Preconditions.checkNotNull(
+ builder.mSatelliteClockModels, "SatelliteClockModels cannot be null");
+ Preconditions.checkNotNull(
+ builder.mSatelliteOrbitModel, "SatelliteOrbitModel cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteHealth, "SatelliteHealth cannot be null");
+ Preconditions.checkNotNull(
+ builder.mSatelliteEphemerisTime, "SatelliteEphemerisTime cannot be null");
+ mSatelliteCodeNumber = builder.mSatelliteCodeNumber;
+ final List<GalileoSatelliteClockModel> satelliteClockModels = builder.mSatelliteClockModels;
+ mSatelliteClockModels = Collections.unmodifiableList(new ArrayList<>(satelliteClockModels));
+ mSatelliteOrbitModel = builder.mSatelliteOrbitModel;
+ mSatelliteHealth = builder.mSatelliteHealth;
+ mSatelliteEphemerisTime = builder.mSatelliteEphemerisTime;
+ }
+
+ /** Returns the satellite code number. */
+ @IntRange(from = 1, to = 36)
+ public int getSatelliteCodeNumber() {
+ return mSatelliteCodeNumber;
+ }
+
+ /** Returns the list of satellite clock models. */
+ @NonNull
+ public List<GalileoSatelliteClockModel> getSatelliteClockModels() {
+ return mSatelliteClockModels;
+ }
+
+ /** Returns the satellite orbit model. */
+ @NonNull
+ public KeplerianOrbitModel getSatelliteOrbitModel() {
+ return mSatelliteOrbitModel;
+ }
+
+ /** Returns the satellite health. */
+ @NonNull
+ public GalileoSvHealth getSatelliteHealth() {
+ return mSatelliteHealth;
+ }
+
+ /** Returns the satellite ephemeris time. */
+ @NonNull
+ public SatelliteEphemerisTime getSatelliteEphemerisTime() {
+ return mSatelliteEphemerisTime;
+ }
+
+ public static final @NonNull Creator<GalileoSatelliteEphemeris> CREATOR =
+ new Creator<GalileoSatelliteEphemeris>() {
+ @Override
+ @NonNull
+ public GalileoSatelliteEphemeris createFromParcel(Parcel in) {
+ final GalileoSatelliteEphemeris.Builder galileoSatelliteEphemeris =
+ new Builder();
+ galileoSatelliteEphemeris.setSatelliteCodeNumber(in.readInt());
+ List<GalileoSatelliteClockModel> satelliteClockModels = new ArrayList<>();
+ in.readTypedList(satelliteClockModels, GalileoSatelliteClockModel.CREATOR);
+ galileoSatelliteEphemeris.setSatelliteClockModels(satelliteClockModels);
+ galileoSatelliteEphemeris.setSatelliteOrbitModel(
+ in.readTypedObject(KeplerianOrbitModel.CREATOR));
+ galileoSatelliteEphemeris.setSatelliteHealth(
+ in.readTypedObject(GalileoSvHealth.CREATOR));
+ galileoSatelliteEphemeris.setSatelliteEphemerisTime(
+ in.readTypedObject(SatelliteEphemerisTime.CREATOR));
+ return galileoSatelliteEphemeris.build();
+ }
+
+ @Override
+ public GalileoSatelliteEphemeris[] newArray(int size) {
+ return new GalileoSatelliteEphemeris[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mSatelliteCodeNumber);
+ parcel.writeTypedList(mSatelliteClockModels, flags);
+ parcel.writeTypedObject(mSatelliteOrbitModel, flags);
+ parcel.writeTypedObject(mSatelliteHealth, flags);
+ parcel.writeTypedObject(mSatelliteEphemerisTime, flags);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GalileoSatelliteEphemeris[");
+ builder.append("satelliteCodeNumber = ").append(mSatelliteCodeNumber);
+ builder.append(", satelliteClockModels = ").append(mSatelliteClockModels);
+ builder.append(", satelliteOrbitModel = ").append(mSatelliteOrbitModel);
+ builder.append(", satelliteHealth = ").append(mSatelliteHealth);
+ builder.append(", satelliteEphemerisTime = ").append(mSatelliteEphemerisTime);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GalileoSatelliteEphemeris}. */
+ public static final class Builder {
+ private int mSatelliteCodeNumber;
+ private List<GalileoSatelliteClockModel> mSatelliteClockModels;
+ private KeplerianOrbitModel mSatelliteOrbitModel;
+ private GalileoSvHealth mSatelliteHealth;
+ private SatelliteEphemerisTime mSatelliteEphemerisTime;
+
+ /** Sets the satellite code number. */
+ @NonNull
+ public Builder setSatelliteCodeNumber(
+ @IntRange(from = 1, to = 36) int satelliteCodeNumber) {
+ mSatelliteCodeNumber = satelliteCodeNumber;
+ return this;
+ }
+
+ /** Sets the array of satellite clock model. */
+ @NonNull
+ public Builder setSatelliteClockModels(
+ @NonNull List<GalileoSatelliteClockModel> satelliteClockModels) {
+ mSatelliteClockModels = satelliteClockModels;
+ return this;
+ }
+
+ /** Sets the satellite orbit model. */
+ @NonNull
+ public Builder setSatelliteOrbitModel(@NonNull KeplerianOrbitModel satelliteOrbitModel) {
+ mSatelliteOrbitModel = satelliteOrbitModel;
+ return this;
+ }
+
+ /** Sets the satellite health. */
+ @NonNull
+ public Builder setSatelliteHealth(@NonNull GalileoSvHealth satelliteHealth) {
+ mSatelliteHealth = satelliteHealth;
+ return this;
+ }
+
+ /** Sets the satellite ephemeris time. */
+ @NonNull
+ public Builder setSatelliteEphemerisTime(
+ @NonNull SatelliteEphemerisTime satelliteEphemerisTime) {
+ mSatelliteEphemerisTime = satelliteEphemerisTime;
+ return this;
+ }
+
+ /** Builds a {@link GalileoSatelliteEphemeris} instance as specified by this builder. */
+ @NonNull
+ public GalileoSatelliteEphemeris build() {
+ return new GalileoSatelliteEphemeris(this);
+ }
+ }
+
+ /**
+ * A class contains the set of parameters needed for Galileo satellite health.
+ *
+ * <p>This is defined in Galileo-OS-SIS-ICD 5.1.9.3.
+ */
+ public static final class GalileoSvHealth implements Parcelable {
+ /** E1-B data validity status. */
+ private int mDataValidityStatusE1b;
+
+ /** E1-B/C signal health status. */
+ private int mSignalHealthStatusE1b;
+
+ /** E5a data validity status. */
+ private int mDataValidityStatusE5a;
+
+ /** E5a signal health status. */
+ private int mSignalHealthStatusE5a;
+
+ /** E5b data validity status. */
+ private int mDataValidityStatusE5b;
+
+ /** E5b signal health status. */
+ private int mSignalHealthStatusE5b;
+
+ private GalileoSvHealth(Builder builder) {
+ Preconditions.checkArgumentInRange(
+ builder.mDataValidityStatusE1b, 0, 1, "DataValidityStatusE1b");
+ Preconditions.checkArgumentInRange(
+ builder.mSignalHealthStatusE1b, 0, 3, "SignalHealthStatusE1b");
+ Preconditions.checkArgumentInRange(
+ builder.mDataValidityStatusE5a, 0, 1, "DataValidityStatusE5a");
+ Preconditions.checkArgumentInRange(
+ builder.mSignalHealthStatusE5a, 0, 3, "SignalHealthStatusE5a");
+ Preconditions.checkArgumentInRange(
+ builder.mDataValidityStatusE5b, 0, 1, "DataValidityStatusE5b");
+ Preconditions.checkArgumentInRange(
+ builder.mSignalHealthStatusE5b, 0, 3, "SignalHealthStatusE5b");
+ mDataValidityStatusE1b = builder.mDataValidityStatusE1b;
+ mSignalHealthStatusE1b = builder.mSignalHealthStatusE1b;
+ mDataValidityStatusE5a = builder.mDataValidityStatusE5a;
+ mSignalHealthStatusE5a = builder.mSignalHealthStatusE5a;
+ mDataValidityStatusE5b = builder.mDataValidityStatusE5b;
+ mSignalHealthStatusE5b = builder.mSignalHealthStatusE5b;
+ }
+
+ /** Returns the E1-B data validity status. */
+ @IntRange(from = 0, to = 1)
+ public int getDataValidityStatusE1b() {
+ return mDataValidityStatusE1b;
+ }
+
+ /** Returns the E1-B/C signal health status. */
+ @IntRange(from = 0, to = 3)
+ public int getSignalHealthStatusE1b() {
+ return mSignalHealthStatusE1b;
+ }
+
+ /** Returns the E5a data validity status. */
+ @IntRange(from = 0, to = 1)
+ public int getDataValidityStatusE5a() {
+ return mDataValidityStatusE5a;
+ }
+
+ /** Returns the E5a signal health status. */
+ @IntRange(from = 0, to = 3)
+ public int getSignalHealthStatusE5a() {
+ return mSignalHealthStatusE5a;
+ }
+
+ /** Returns the E5b data validity status. */
+ @IntRange(from = 0, to = 1)
+ public int getDataValidityStatusE5b() {
+ return mDataValidityStatusE5b;
+ }
+
+ /** Returns the E5b signal health status. */
+ @IntRange(from = 0, to = 3)
+ public int getSignalHealthStatusE5b() {
+ return mSignalHealthStatusE5b;
+ }
+
+ public static final @NonNull Creator<GalileoSvHealth> CREATOR =
+ new Creator<GalileoSvHealth>() {
+ @Override
+ @NonNull
+ public GalileoSvHealth createFromParcel(Parcel in) {
+ final GalileoSvHealth.Builder galileoSvHealth = new Builder();
+ galileoSvHealth.setDataValidityStatusE1b(in.readInt());
+ galileoSvHealth.setSignalHealthStatusE1b(in.readInt());
+ galileoSvHealth.setDataValidityStatusE5a(in.readInt());
+ galileoSvHealth.setSignalHealthStatusE5a(in.readInt());
+ galileoSvHealth.setDataValidityStatusE5b(in.readInt());
+ galileoSvHealth.setSignalHealthStatusE5b(in.readInt());
+ return galileoSvHealth.build();
+ }
+
+ @Override
+ public GalileoSvHealth[] newArray(int size) {
+ return new GalileoSvHealth[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mDataValidityStatusE1b);
+ parcel.writeInt(mSignalHealthStatusE1b);
+ parcel.writeInt(mDataValidityStatusE5a);
+ parcel.writeInt(mSignalHealthStatusE5a);
+ parcel.writeInt(mDataValidityStatusE5b);
+ parcel.writeInt(mSignalHealthStatusE5b);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GalileoSvHealth[");
+ builder.append("dataValidityStatusE1b = ").append(mDataValidityStatusE1b);
+ builder.append(", signalHealthStatusE1b = ").append(mSignalHealthStatusE1b);
+ builder.append(", dataValidityStatusE5a = ").append(mDataValidityStatusE5a);
+ builder.append(", signalHealthStatusE5a = ").append(mSignalHealthStatusE5a);
+ builder.append(", dataValidityStatusE5b = ").append(mDataValidityStatusE5b);
+ builder.append(", signalHealthStatusE5b = ").append(mSignalHealthStatusE5b);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GalileoSvHealth}. */
+ public static final class Builder {
+ private int mDataValidityStatusE1b;
+ private int mSignalHealthStatusE1b;
+ private int mDataValidityStatusE5a;
+ private int mSignalHealthStatusE5a;
+ private int mDataValidityStatusE5b;
+ private int mSignalHealthStatusE5b;
+
+ /** Sets the E1-B data validity status. */
+ @NonNull
+ public Builder setDataValidityStatusE1b(
+ @IntRange(from = 0, to = 1) int dataValidityStatusE1b) {
+ mDataValidityStatusE1b = dataValidityStatusE1b;
+ return this;
+ }
+
+ /** Sets the E1-B/C signal health status. */
+ @NonNull
+ public Builder setSignalHealthStatusE1b(
+ @IntRange(from = 0, to = 3) int signalHealthStatusE1b) {
+ mSignalHealthStatusE1b = signalHealthStatusE1b;
+ return this;
+ }
+
+ /** Sets the E5a data validity status. */
+ @NonNull
+ public Builder setDataValidityStatusE5a(
+ @IntRange(from = 0, to = 1) int dataValidityStatusE5a) {
+ mDataValidityStatusE5a = dataValidityStatusE5a;
+ return this;
+ }
+
+ /** Sets the E5a signal health status. */
+ @NonNull
+ public Builder setSignalHealthStatusE5a(
+ @IntRange(from = 0, to = 3) int signalHealthStatusE5a) {
+ mSignalHealthStatusE5a = signalHealthStatusE5a;
+ return this;
+ }
+
+ /** Sets the E5b data validity status. */
+ @NonNull
+ public Builder setDataValidityStatusE5b(
+ @IntRange(from = 0, to = 1) int dataValidityStatusE5b) {
+ mDataValidityStatusE5b = dataValidityStatusE5b;
+ return this;
+ }
+
+ /** Sets the E5b signal health status. */
+ @NonNull
+ public Builder setSignalHealthStatusE5b(
+ @IntRange(from = 0, to = 3) int signalHealthStatusE5b) {
+ mSignalHealthStatusE5b = signalHealthStatusE5b;
+ return this;
+ }
+
+ /** Builds a {@link GalileoSvHealth}. */
+ @NonNull
+ public GalileoSvHealth build() {
+ return new GalileoSvHealth(this);
+ }
+ }
+ }
+
+ /**
+ * A class contains the set of parameters needed for Galileo satellite clock correction.
+ *
+ * <p>This is defined in Galileo-OS-SIS-ICD 5.1.3.
+ */
+ public static final class GalileoSatelliteClockModel implements Parcelable {
+
+ /**
+ * The type of the satellite clock.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({TYPE_UNDEFINED, TYPE_FNAV, TYPE_INAV})
+ public @interface SatelliteClockType {}
+
+ /**
+ * The following enumerations must be in sync with the values declared in
+ * GalileoSatelliteEphemeris.aidl.
+ */
+
+ /** The type of the satellite clock is unknown. */
+ public static final int TYPE_UNDEFINED = 0;
+
+ /** The type of the satellite clock is FNAV. */
+ public static final int TYPE_FNAV = 1;
+
+ /** The type of the satellite clock is INAV. */
+ public static final int TYPE_INAV = 2;
+
+ /**
+ * Time of the clock in seconds since Galileo epoch.
+ *
+ * <p>Corresponds to the 'Epoch' field within the 'SV/EPOCH/SV CLK' record of Galileo
+ * navigation message in RINEX 3.05 Table A8.
+ */
+ private final long mTimeOfClockSeconds;
+
+ /** SV clock bias correction coefficient in seconds. */
+ private double mAf0;
+
+ /** SV clock drift correction coefficient in seconds per second. */
+ private double mAf1;
+
+ /** SV clock drift rate correction coefficient in seconds per second squared. */
+ private double mAf2;
+
+ /**
+ * Broadcast group delay in seconds.
+ *
+ * <p>This is defined in Galileo-OS-SIS-ICD 5.1.5.
+ */
+ private double mBgdSeconds;
+
+ /**
+ * Signal in space accuracy in meters.
+ *
+ * <p>This is defined in Galileo-OS-SIS-ICD 5.1.12.
+ */
+ private double mSisaMeters;
+
+ /** Type of satellite clock . */
+ private final @SatelliteClockType int mSatelliteClockType;
+
+ private GalileoSatelliteClockModel(Builder builder) {
+ Preconditions.checkArgument(builder.mTimeOfClockSeconds >= 0);
+ Preconditions.checkArgumentInRange(builder.mAf0, -0.0625f, 0.0625f, "AF0");
+ Preconditions.checkArgumentInRange(builder.mAf1, -1.5e-8f, 1.5e-8f, "AF1");
+ Preconditions.checkArgumentInRange(builder.mAf2, -5.56e-17f, 5.56e-17f, "AF2");
+ Preconditions.checkArgumentInRange(
+ builder.mBgdSeconds, -1.2e-7f, 1.2e-7f, "BgdSeconds");
+ Preconditions.checkArgument(builder.mSisaMeters >= 0.0f);
+ Preconditions.checkArgumentInRange(
+ builder.mSatelliteClockType, TYPE_UNDEFINED, TYPE_INAV, "SatelliteClockType");
+ mTimeOfClockSeconds = builder.mTimeOfClockSeconds;
+ mAf0 = builder.mAf0;
+ mAf1 = builder.mAf1;
+ mAf2 = builder.mAf2;
+ mBgdSeconds = builder.mBgdSeconds;
+ mSisaMeters = builder.mSisaMeters;
+ mSatelliteClockType = builder.mSatelliteClockType;
+ }
+
+ /** Returns the time of the clock in seconds since Galileo epoch. */
+ @IntRange(from = 0)
+ public long getTimeOfClockSeconds() {
+ return mTimeOfClockSeconds;
+ }
+
+ /** Returns the SV clock bias correction coefficient in seconds. */
+ @FloatRange(from = -0.0625f, to = 0.0625f)
+ public double getAf0() {
+ return mAf0;
+ }
+
+ /** Returns the SV clock drift correction coefficient in seconds per second. */
+ @FloatRange(from = -1.5e-8f, to = 1.5e-8f)
+ public double getAf1() {
+ return mAf1;
+ }
+
+ /** Returns the SV clock drift rate correction coefficient in seconds per second squared. */
+ @FloatRange(from = -5.56e-17f, to = 5.56e-17f)
+ public double getAf2() {
+ return mAf2;
+ }
+
+ /** Returns the broadcast group delay in seconds. */
+ @FloatRange(from = -1.2e-7f, to = 1.2e-7f)
+ public double getBgdSeconds() {
+ return mBgdSeconds;
+ }
+
+ /** Returns the signal in space accuracy in meters. */
+ @FloatRange(from = 0.0f)
+ public double getSisaMeters() {
+ return mSisaMeters;
+ }
+
+ /** Returns the type of satellite clock. */
+ public @SatelliteClockType int getSatelliteClockType() {
+ return mSatelliteClockType;
+ }
+
+ public static final @NonNull Creator<GalileoSatelliteClockModel> CREATOR =
+ new Creator<GalileoSatelliteClockModel>() {
+ @Override
+ @NonNull
+ public GalileoSatelliteClockModel createFromParcel(Parcel in) {
+ final GalileoSatelliteClockModel.Builder galileoSatelliteClockModel =
+ new Builder();
+ galileoSatelliteClockModel.setTimeOfClockSeconds(in.readLong());
+ galileoSatelliteClockModel.setAf0(in.readDouble());
+ galileoSatelliteClockModel.setAf1(in.readDouble());
+ galileoSatelliteClockModel.setAf2(in.readDouble());
+ galileoSatelliteClockModel.setBgdSeconds(in.readDouble());
+ galileoSatelliteClockModel.setSisaMeters(in.readDouble());
+ galileoSatelliteClockModel.setSatelliteClockType(in.readInt());
+ return galileoSatelliteClockModel.build();
+ }
+
+ @Override
+ public GalileoSatelliteClockModel[] newArray(int size) {
+ return new GalileoSatelliteClockModel[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeLong(mTimeOfClockSeconds);
+ parcel.writeDouble(mAf0);
+ parcel.writeDouble(mAf1);
+ parcel.writeDouble(mAf2);
+ parcel.writeDouble(mBgdSeconds);
+ parcel.writeDouble(mSisaMeters);
+ parcel.writeInt(mSatelliteClockType);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GalileoSatelliteClockModel[");
+ builder.append("timeOfClockSeconds = ").append(mTimeOfClockSeconds);
+ builder.append(", af0 = ").append(mAf0);
+ builder.append(", af1 = ").append(mAf1);
+ builder.append(", af2 = ").append(mAf2);
+ builder.append(", bgdSeconds = ").append(mBgdSeconds);
+ builder.append(", sisaMeters = ").append(mSisaMeters);
+ builder.append(", satelliteClockType = ").append(mSatelliteClockType);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GalileoSatelliteClockModel}. */
+ public static final class Builder {
+ private long mTimeOfClockSeconds;
+ private double mAf0;
+ private double mAf1;
+ private double mAf2;
+ private double mBgdSeconds;
+ private double mSisaMeters;
+ private @SatelliteClockType int mSatelliteClockType;
+
+ /** Sets the time of the clock in seconds since Galileo epoch. */
+ @NonNull
+ public Builder setTimeOfClockSeconds(@IntRange(from = 0) long timeOfClockSeconds) {
+ mTimeOfClockSeconds = timeOfClockSeconds;
+ return this;
+ }
+
+ /** Sets the SV clock bias correction coefficient in seconds. */
+ @NonNull
+ public Builder setAf0(@FloatRange(from = -0.0625f, to = 0.0625f) double af0) {
+ mAf0 = af0;
+ return this;
+ }
+
+ /** Sets the SV clock drift correction coefficient in seconds per second. */
+ @NonNull
+ public Builder setAf1(@FloatRange(from = -1.5e-8f, to = 1.5e-8f) double af1) {
+ mAf1 = af1;
+ return this;
+ }
+
+ /**
+ * Sets the SV clock drift rate correction coefficient in seconds per second squared.
+ */
+ @NonNull
+ public Builder setAf2(@FloatRange(from = -5.56e-17f, to = 5.56e-17f) double af2) {
+ mAf2 = af2;
+ return this;
+ }
+
+ /** Sets the broadcast group delay in seconds. */
+ @NonNull
+ public Builder setBgdSeconds(
+ @FloatRange(from = -1.2e-7f, to = 1.2e-7f) double bgdSeconds) {
+ mBgdSeconds = bgdSeconds;
+ return this;
+ }
+
+ /** Sets the signal in space accuracy in meters. */
+ @NonNull
+ public Builder setSisaMeters(@FloatRange(from = 0.0f) double sisaMeters) {
+ mSisaMeters = sisaMeters;
+ return this;
+ }
+
+ /** Sets the type of satellite clock. */
+ @NonNull
+ public Builder setSatelliteClockType(@SatelliteClockType int satelliteClockType) {
+ mSatelliteClockType = satelliteClockType;
+ return this;
+ }
+
+ /**
+ * Builds a {@link GalileoSatelliteClockModel} instance as specified by this builder.
+ */
+ @NonNull
+ public GalileoSatelliteClockModel build() {
+ return new GalileoSatelliteClockModel(this);
+ }
+ }
+ }
+}
diff --git a/location/java/android/location/GlonassAlmanac.java b/location/java/android/location/GlonassAlmanac.java
new file mode 100644
index 0000000..861dc5d
--- /dev/null
+++ b/location/java/android/location/GlonassAlmanac.java
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class contains Glonass almanac data.
+ *
+ * <p>This is defined in Glonass ICD v5.1 section 4.5.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GlonassAlmanac implements Parcelable {
+
+ /** Almanac issue date in milliseconds (UTC) */
+ private final long mIssueDateMillis;
+
+ /** List of GlonassSatelliteAlmanacs. */
+ @NonNull private final List<GlonassSatelliteAlmanac> mSatelliteAlmanacs;
+
+ /**
+ * Constructor for GlonassAlmanac.
+ *
+ * @param issueDateMillis The almanac issue date in milliseconds (UTC).
+ * @param satelliteAlmanacs The list of GlonassSatelliteAlmanac.
+ */
+ public GlonassAlmanac(
+ @IntRange(from = 0) long issueDateMillis,
+ @NonNull List<GlonassSatelliteAlmanac> satelliteAlmanacs) {
+ Preconditions.checkArgument(issueDateMillis >= 0);
+ Preconditions.checkNotNull(satelliteAlmanacs, "satelliteAlmanacs cannot be null");
+ mIssueDateMillis = issueDateMillis;
+ mSatelliteAlmanacs = Collections.unmodifiableList(new ArrayList<>(satelliteAlmanacs));
+ }
+
+ /** Returns the almanac issue date in milliseconds (UTC). */
+ @IntRange(from = 0)
+ public long getIssueDateMillis() {
+ return mIssueDateMillis;
+ }
+
+ /** Returns the list of GlonassSatelliteAlmanacs. */
+ @NonNull
+ public List<GlonassSatelliteAlmanac> getSatelliteAlmanacs() {
+ return mSatelliteAlmanacs;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mIssueDateMillis);
+ dest.writeTypedList(mSatelliteAlmanacs);
+ }
+
+ public static final @NonNull Parcelable.Creator<GlonassAlmanac> CREATOR =
+ new Parcelable.Creator<GlonassAlmanac>() {
+ @Override
+ public GlonassAlmanac createFromParcel(@NonNull Parcel in) {
+ long issueDateMillis = in.readLong();
+ List<GlonassSatelliteAlmanac> satelliteAlmanacs = new ArrayList<>();
+ in.readTypedList(satelliteAlmanacs, GlonassSatelliteAlmanac.CREATOR);
+ return new GlonassAlmanac(issueDateMillis, satelliteAlmanacs);
+ }
+
+ @Override
+ public GlonassAlmanac[] newArray(int size) {
+ return new GlonassAlmanac[size];
+ }
+ };
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GlonassAlmanac[");
+ builder.append("issueDateMillis = ").append(mIssueDateMillis);
+ builder.append(", satelliteAlmanacs = ").append(mSatelliteAlmanacs);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /**
+ * A class contains Glonass satellite almanac data.
+ *
+ * <p>This is defined in Glonass ICD v5.1 section 4.5.
+ */
+ public static final class GlonassSatelliteAlmanac implements Parcelable {
+ /** Slot number. */
+ private final int mSlotNumber;
+
+ /** Satellite health information (0=healthy, 1=unhealthy). */
+ private final int mSvHealth;
+
+ /** Frequency channel number. */
+ private final int mFreqChannel;
+
+ /** Coarse value of satellite time correction to GLONASS time in seconds. */
+ private final double mTau;
+
+ /** Time of first ascending node passage of satellite in seconds. */
+ private final double mTLambda;
+
+ /** Longitude of the first ascending node in semi-circles. */
+ private final double mLambda;
+
+ /** Correction to the mean value of inclination in semi-circles. */
+ private final double mDeltaI;
+
+ /** Correction to the mean value of the draconian period in seconds per orbital period */
+ private final double mDeltaT;
+
+ /** Rate of change of draconian period in seconds per orbital period squared. */
+ private final double mDeltaTDot;
+
+ /** Eccentricity. */
+ private final double mEccentricity;
+
+ /** Argument of perigee in radians. */
+ private final double mOmega;
+
+ private GlonassSatelliteAlmanac(Builder builder) {
+ // Allow slotNumber beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mSlotNumber >= 1);
+ // Allow svHealth beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mSvHealth >= 0);
+ Preconditions.checkArgumentInRange(builder.mFreqChannel, 0, 31, "FreqChannel");
+ Preconditions.checkArgumentInRange(builder.mTau, -1.9e-3f, 1.9e-3f, "Tau");
+ Preconditions.checkArgumentInRange(builder.mTLambda, 0.0f, 44100.0f, "TLambda");
+ Preconditions.checkArgumentInRange(builder.mLambda, -1.0f, 1.0f, "Lambda");
+ Preconditions.checkArgumentInRange(builder.mDeltaI, -0.067f, 0.067f, "DeltaI");
+ Preconditions.checkArgumentInRange(builder.mDeltaT, -3600.0f, 3600.0f, "DeltaT");
+ Preconditions.checkArgumentInRange(builder.mDeltaTDot, -0.004f, 0.004f, "DeltaTDot");
+ Preconditions.checkArgumentInRange(builder.mEccentricity, 0.0f, 0.03f, "Eccentricity");
+ Preconditions.checkArgumentInRange(builder.mOmega, -1.0f, 1.0f, "Omega");
+ mSlotNumber = builder.mSlotNumber;
+ mSvHealth = builder.mSvHealth;
+ mFreqChannel = builder.mFreqChannel;
+ mTau = builder.mTau;
+ mTLambda = builder.mTLambda;
+ mLambda = builder.mLambda;
+ mDeltaI = builder.mDeltaI;
+ mDeltaT = builder.mDeltaT;
+ mDeltaTDot = builder.mDeltaTDot;
+ mEccentricity = builder.mEccentricity;
+ mOmega = builder.mOmega;
+ }
+
+ /** Returns the slot number. */
+ @IntRange(from = 1, to = 25)
+ public int getSlotNumber() {
+ return mSlotNumber;
+ }
+
+ /** Returns the Satellite health information (0=healthy, 1=unhealthy). */
+ @IntRange(from = 0, to = 1)
+ public int getSvHealth() {
+ return mSvHealth;
+ }
+
+ /** Returns the frequency channel number. */
+ @IntRange(from = 0, to = 31)
+ public int getFreqChannel() {
+ return mFreqChannel;
+ }
+
+ /** Returns the coarse value of satellite time correction to GLONASS time in seconds. */
+ @FloatRange(from = -1.9e-3f, to = 1.9e-3f)
+ public double getTau() {
+ return mTau;
+ }
+
+ /** Returns the time of first ascending node passage of satellite in seconds. */
+ @FloatRange(from = 0.0f, to = 44100.0f)
+ public double getTLambda() {
+ return mTLambda;
+ }
+
+ /** Returns the longitude of the first ascending node in semi-circles. */
+ @FloatRange(from = -1.0f, to = 1.0f)
+ public double getLambda() {
+ return mLambda;
+ }
+
+ /** Returns the correction to the mean value of inclination in semi-circles. */
+ @FloatRange(from = -0.067f, to = 0.067f)
+ public double getDeltaI() {
+ return mDeltaI;
+ }
+
+ /**
+ * Returns the correction to the mean value of the draconian period in seconds per orbital
+ * period
+ */
+ @FloatRange(from = -3600.0f, to = 3600.0f)
+ public double getDeltaT() {
+ return mDeltaT;
+ }
+
+ /** Returns the rate of change of draconian period in seconds per orbital period squared. */
+ @FloatRange(from = -0.004f, to = 0.004f)
+ public double getDeltaTDot() {
+ return mDeltaTDot;
+ }
+
+ /** Returns the eccentricity. */
+ @FloatRange(from = 0.0f, to = 0.03f)
+ public double getEccentricity() {
+ return mEccentricity;
+ }
+
+ /** Returns the argument of perigee in radians. */
+ @FloatRange(from = -1.0f, to = 1.0f)
+ public double getOmega() {
+ return mOmega;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mSlotNumber);
+ dest.writeInt(mSvHealth);
+ dest.writeInt(mFreqChannel);
+ dest.writeDouble(mTau);
+ dest.writeDouble(mTLambda);
+ dest.writeDouble(mLambda);
+ dest.writeDouble(mDeltaI);
+ dest.writeDouble(mDeltaT);
+ dest.writeDouble(mDeltaTDot);
+ dest.writeDouble(mEccentricity);
+ dest.writeDouble(mOmega);
+ }
+
+ public static final @NonNull Parcelable.Creator<GlonassSatelliteAlmanac> CREATOR =
+ new Parcelable.Creator<GlonassSatelliteAlmanac>() {
+ @Override
+ public GlonassSatelliteAlmanac createFromParcel(@NonNull Parcel source) {
+ return new GlonassSatelliteAlmanac.Builder()
+ .setSlotNumber(source.readInt())
+ .setSvHealth(source.readInt())
+ .setFreqChannel(source.readInt())
+ .setTau(source.readDouble())
+ .setTLambda(source.readDouble())
+ .setLambda(source.readDouble())
+ .setDeltaI(source.readDouble())
+ .setDeltaT(source.readDouble())
+ .setDeltaTDot(source.readDouble())
+ .setEccentricity(source.readDouble())
+ .setOmega(source.readDouble())
+ .build();
+ }
+
+ @Override
+ public GlonassSatelliteAlmanac[] newArray(int size) {
+ return new GlonassSatelliteAlmanac[size];
+ }
+ };
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GlonassSatelliteAlmanac[");
+ builder.append("slotNumber = ").append(mSlotNumber);
+ builder.append(", svHealth = ").append(mSvHealth);
+ builder.append(", freqChannel = ").append(mFreqChannel);
+ builder.append(", tau = ").append(mTau);
+ builder.append(", tLambda = ").append(mTLambda);
+ builder.append(", lambda = ").append(mLambda);
+ builder.append(", deltaI = ").append(mDeltaI);
+ builder.append(", deltaT = ").append(mDeltaT);
+ builder.append(", deltaTDot = ").append(mDeltaTDot);
+ builder.append(", eccentricity = ").append(mEccentricity);
+ builder.append(", omega = ").append(mOmega);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GlonassSatelliteAlmanac}. */
+ public static final class Builder {
+ private int mSlotNumber;
+ private int mSvHealth;
+ private int mFreqChannel;
+ private double mTau;
+ private double mTLambda;
+ private double mLambda;
+ private double mDeltaI;
+ private double mDeltaT;
+ private double mDeltaTDot;
+ private double mEccentricity;
+ private double mOmega;
+
+ /** Sets the slot number. */
+ @NonNull
+ public Builder setSlotNumber(@IntRange(from = 1, to = 25) int slotNumber) {
+ mSlotNumber = slotNumber;
+ return this;
+ }
+
+ /** Sets the Satellite health information (0=healthy, 1=unhealthy). */
+ @NonNull
+ public Builder setSvHealth(@IntRange(from = 0, to = 1) int svHealth) {
+ mSvHealth = svHealth;
+ return this;
+ }
+
+ /** Sets the frequency channel number. */
+ @NonNull
+ public Builder setFreqChannel(@IntRange(from = 0, to = 31) int freqChannel) {
+ mFreqChannel = freqChannel;
+ return this;
+ }
+
+ /** Sets the coarse value of satellite time correction to GLONASS time in seconds. */
+ @NonNull
+ public Builder setTau(@FloatRange(from = -1.9e-3f, to = 1.9e-3f) double tau) {
+ mTau = tau;
+ return this;
+ }
+
+ /** Sets the time of first ascending node passage of satellite in seconds. */
+ @NonNull
+ public Builder setTLambda(@FloatRange(from = 0.0f, to = 44100.0f) double tLambda) {
+ mTLambda = tLambda;
+ return this;
+ }
+
+ /** Sets the longitude of the first ascending node in semi-circles. */
+ @NonNull
+ public Builder setLambda(@FloatRange(from = -1.0f, to = 1.0f) double lambda) {
+ mLambda = lambda;
+ return this;
+ }
+
+ /** Sets the correction to the mean value of inclination in semi-circles. */
+ @NonNull
+ public Builder setDeltaI(@FloatRange(from = -0.067f, to = 0.067f) double deltaI) {
+ mDeltaI = deltaI;
+ return this;
+ }
+
+ /**
+ * Sets the correction to the mean value of the draconian period in seconds per orbital
+ * period.
+ */
+ @NonNull
+ public Builder setDeltaT(@FloatRange(from = -3600.0f, to = 3600.0f) double deltaT) {
+ mDeltaT = deltaT;
+ return this;
+ }
+
+ /**
+ * Sets the rate of change of draconian period in seconds per orbital period squared.
+ */
+ @NonNull
+ public Builder setDeltaTDot(@FloatRange(from = -0.004f, to = 0.004f) double deltaTDot) {
+ mDeltaTDot = deltaTDot;
+ return this;
+ }
+
+ /** Sets the eccentricity. */
+ @NonNull
+ public Builder setEccentricity(
+ @FloatRange(from = 0.0f, to = 0.03f) double eccentricity) {
+ mEccentricity = eccentricity;
+ return this;
+ }
+
+ /** Sets the argument of perigee in radians. */
+ @NonNull
+ public Builder setOmega(@FloatRange(from = -1.0f, to = 1.0f) double omega) {
+ mOmega = omega;
+ return this;
+ }
+
+ /** Builds a {@link GlonassSatelliteAlmanac}. */
+ @NonNull
+ public GlonassSatelliteAlmanac build() {
+ return new GlonassSatelliteAlmanac(this);
+ }
+ }
+ }
+}
diff --git a/location/java/android/location/GlonassAssistance.java b/location/java/android/location/GlonassAssistance.java
new file mode 100644
index 0000000..cc08201
--- /dev/null
+++ b/location/java/android/location/GlonassAssistance.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.location.GnssAssistance.GnssSatelliteCorrections;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class contains Glonass assistance.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GlonassAssistance implements Parcelable {
+
+ /** The Glonass almanac. */
+ @Nullable private final GlonassAlmanac mAlmanac;
+
+ /** The UTC model. */
+ @Nullable private final UtcModel mUtcModel;
+
+ /** The list of time models. */
+ @NonNull private final List<TimeModel> mTimeModels;
+
+ /** The list of Glonass ephemeris. */
+ @NonNull private final List<GlonassSatelliteEphemeris> mSatelliteEphemeris;
+
+ /** The list of Glonass satellite corrections. */
+ @NonNull private final List<GnssSatelliteCorrections> mSatelliteCorrections;
+
+ private GlonassAssistance(Builder builder) {
+ mAlmanac = builder.mAlmanac;
+ mUtcModel = builder.mUtcModel;
+ if (builder.mTimeModels != null) {
+ mTimeModels = Collections.unmodifiableList(new ArrayList<>(builder.mTimeModels));
+ } else {
+ mTimeModels = new ArrayList<>();
+ }
+ if (builder.mSatelliteEphemeris != null) {
+ mSatelliteEphemeris =
+ Collections.unmodifiableList(new ArrayList<>(builder.mSatelliteEphemeris));
+ } else {
+ mSatelliteEphemeris = new ArrayList<>();
+ }
+ if (builder.mSatelliteCorrections != null) {
+ mSatelliteCorrections =
+ Collections.unmodifiableList(new ArrayList<>(builder.mSatelliteCorrections));
+ } else {
+ mSatelliteCorrections = new ArrayList<>();
+ }
+ }
+
+ /** Returns the Glonass almanac. */
+ @Nullable
+ public GlonassAlmanac getAlmanac() {
+ return mAlmanac;
+ }
+
+ /** Returns the UTC model. */
+ @Nullable
+ public UtcModel getUtcModel() {
+ return mUtcModel;
+ }
+
+ /** Returns the list of time models. */
+ @NonNull
+ public List<TimeModel> getTimeModels() {
+ return mTimeModels;
+ }
+
+ /** Returns the list of Glonass satellite ephemeris. */
+ @NonNull
+ public List<GlonassSatelliteEphemeris> getSatelliteEphemeris() {
+ return mSatelliteEphemeris;
+ }
+
+ /** Returns the list of Glonass satellite corrections. */
+ @NonNull
+ public List<GnssSatelliteCorrections> getSatelliteCorrections() {
+ return mSatelliteCorrections;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(mAlmanac, flags);
+ dest.writeTypedObject(mUtcModel, flags);
+ dest.writeTypedList(mTimeModels);
+ dest.writeTypedList(mSatelliteEphemeris);
+ dest.writeTypedList(mSatelliteCorrections);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GlonassAssistance[");
+ builder.append("almanac = ").append(mAlmanac);
+ builder.append(", utcModel = ").append(mUtcModel);
+ builder.append(", timeModels = ").append(mTimeModels);
+ builder.append(", satelliteEphemeris = ").append(mSatelliteEphemeris);
+ builder.append(", satelliteCorrections = ").append(mSatelliteCorrections);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ public static final @NonNull Creator<GlonassAssistance> CREATOR =
+ new Creator<GlonassAssistance>() {
+ @Override
+ public GlonassAssistance createFromParcel(Parcel in) {
+ return new GlonassAssistance.Builder()
+ .setAlmanac(in.readTypedObject(GlonassAlmanac.CREATOR))
+ .setUtcModel(in.readTypedObject(UtcModel.CREATOR))
+ .setTimeModels(in.createTypedArrayList(TimeModel.CREATOR))
+ .setSatelliteEphemeris(
+ in.createTypedArrayList(GlonassSatelliteEphemeris.CREATOR))
+ .setSatelliteCorrections(
+ in.createTypedArrayList(GnssSatelliteCorrections.CREATOR))
+ .build();
+ }
+
+ @Override
+ public GlonassAssistance[] newArray(int size) {
+ return new GlonassAssistance[size];
+ }
+ };
+
+ /** Builder for {@link GlonassAssistance}. */
+ public static final class Builder {
+ private GlonassAlmanac mAlmanac;
+ private UtcModel mUtcModel;
+ private List<TimeModel> mTimeModels;
+ private List<GlonassSatelliteEphemeris> mSatelliteEphemeris;
+ private List<GnssSatelliteCorrections> mSatelliteCorrections;
+
+ /** Sets the Glonass almanac. */
+ @NonNull
+ public Builder setAlmanac(
+ @Nullable @SuppressLint("NullableCollection") GlonassAlmanac almanac) {
+ mAlmanac = almanac;
+ return this;
+ }
+
+ /** Sets the UTC model. */
+ @NonNull
+ public Builder setUtcModel(
+ @Nullable @SuppressLint("NullableCollection") UtcModel utcModel) {
+ mUtcModel = utcModel;
+ return this;
+ }
+
+ /** Sets the list of time models. */
+ @NonNull
+ public Builder setTimeModels(
+ @Nullable @SuppressLint("NullableCollection") List<TimeModel> timeModels) {
+ mTimeModels = timeModels;
+ return this;
+ }
+
+ /** Sets the list of Glonass satellite ephemeris. */
+ @NonNull
+ public Builder setSatelliteEphemeris(
+ @Nullable @SuppressLint("NullableCollection")
+ List<GlonassSatelliteEphemeris> satelliteEphemeris) {
+ mSatelliteEphemeris = satelliteEphemeris;
+ return this;
+ }
+
+ /** Sets the list of Glonass satellite corrections. */
+ @NonNull
+ public Builder setSatelliteCorrections(
+ @Nullable @SuppressLint("NullableCollection")
+ List<GnssSatelliteCorrections> satelliteCorrections) {
+ mSatelliteCorrections = satelliteCorrections;
+ return this;
+ }
+
+ /** Builds the {@link GlonassAssistance}. */
+ @NonNull
+ public GlonassAssistance build() {
+ return new GlonassAssistance(this);
+ }
+ }
+}
diff --git a/location/java/android/location/GlonassSatelliteEphemeris.java b/location/java/android/location/GlonassSatelliteEphemeris.java
new file mode 100644
index 0000000..77a6ebb
--- /dev/null
+++ b/location/java/android/location/GlonassSatelliteEphemeris.java
@@ -0,0 +1,623 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class contains ephemeris parameters specific to Glonass satellites.
+ *
+ * <p>This is defined in RINEX 3.05 APPENDIX 10 and Glonass ICD v5.1 section 4.4.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GlonassSatelliteEphemeris implements Parcelable {
+
+ /** L1/Satellite system (R), satellite number (slot number in sat. constellation). */
+ private final int mSlotNumber;
+
+ /** Health state (0=healthy, 1=unhealthy). */
+ private final int mHealthState;
+
+ /** Message frame time in seconds of the UTC week (tk+nd*86400). */
+ private final double mFrameTimeSeconds;
+
+ /** Age of current information in days (E). */
+ private final int mAgeInDays;
+
+ /** Satellite clock model. */
+ @NonNull private final GlonassSatelliteClockModel mSatelliteClockModel;
+
+ /** Satellite orbit model. */
+ @NonNull private final GlonassSatelliteOrbitModel mSatelliteOrbitModel;
+
+ private GlonassSatelliteEphemeris(Builder builder) {
+ // Allow SlotNumber beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mSlotNumber >= 1);
+ // Allow HealthState beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mHealthState >= 0);
+ Preconditions.checkArgument(builder.mFrameTimeSeconds >= 0.0f);
+ Preconditions.checkArgumentInRange(builder.mAgeInDays, 0, 31, "AgeInDays");
+ Preconditions.checkNotNull(
+ builder.mSatelliteClockModel, "SatelliteClockModel cannot be null");
+ Preconditions.checkNotNull(
+ builder.mSatelliteOrbitModel, "SatelliteOrbitModel cannot be null");
+ mSlotNumber = builder.mSlotNumber;
+ mHealthState = builder.mHealthState;
+ mFrameTimeSeconds = builder.mFrameTimeSeconds;
+ mAgeInDays = builder.mAgeInDays;
+ mSatelliteClockModel = builder.mSatelliteClockModel;
+ mSatelliteOrbitModel = builder.mSatelliteOrbitModel;
+ }
+
+ /**
+ * Returns the L1/Satellite system (R), satellite number (slot number in sat. constellation).
+ */
+ @IntRange(from = 1, to = 25)
+ public int getSlotNumber() {
+ return mSlotNumber;
+ }
+
+ /** Returns the health state (0=healthy, 1=unhealthy). */
+ @IntRange(from = 0, to = 1)
+ public int getHealthState() {
+ return mHealthState;
+ }
+
+ /** Returns the message frame time in seconds of the UTC week (tk+nd*86400). */
+ @FloatRange(from = 0.0f)
+ public double getFrameTimeSeconds() {
+ return mFrameTimeSeconds;
+ }
+
+ /** Returns the age of current information in days (E). */
+ @IntRange(from = 0, to = 31)
+ public int getAgeInDays() {
+ return mAgeInDays;
+ }
+
+ /** Returns the satellite clock model. */
+ @NonNull
+ public GlonassSatelliteClockModel getSatelliteClockModel() {
+ return mSatelliteClockModel;
+ }
+
+ /** Returns the satellite orbit model. */
+ @NonNull
+ public GlonassSatelliteOrbitModel getSatelliteOrbitModel() {
+ return mSatelliteOrbitModel;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mSlotNumber);
+ dest.writeInt(mHealthState);
+ dest.writeDouble(mFrameTimeSeconds);
+ dest.writeInt(mAgeInDays);
+ dest.writeTypedObject(mSatelliteClockModel, flags);
+ dest.writeTypedObject(mSatelliteOrbitModel, flags);
+ }
+
+ public static final @NonNull Parcelable.Creator<GlonassSatelliteEphemeris> CREATOR =
+ new Parcelable.Creator<GlonassSatelliteEphemeris>() {
+ @Override
+ public GlonassSatelliteEphemeris createFromParcel(@NonNull Parcel source) {
+ return new GlonassSatelliteEphemeris.Builder()
+ .setSlotNumber(source.readInt())
+ .setHealthState(source.readInt())
+ .setFrameTimeSeconds(source.readDouble())
+ .setAgeInDays(source.readInt())
+ .setSatelliteClockModel(
+ source.readTypedObject(GlonassSatelliteClockModel.CREATOR))
+ .setSatelliteOrbitModel(
+ source.readTypedObject(GlonassSatelliteOrbitModel.CREATOR))
+ .build();
+ }
+
+ @Override
+ public GlonassSatelliteEphemeris[] newArray(int size) {
+ return new GlonassSatelliteEphemeris[size];
+ }
+ };
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GlonassSatelliteEphemeris[");
+ builder.append("slotNumber = ").append(mSlotNumber);
+ builder.append(", healthState = ").append(mHealthState);
+ builder.append(", frameTimeSeconds = ").append(mFrameTimeSeconds);
+ builder.append(", ageInDays = ").append(mAgeInDays);
+ builder.append(", satelliteClockModel = ").append(mSatelliteClockModel);
+ builder.append(", satelliteOrbitModel = ").append(mSatelliteOrbitModel);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GlonassSatelliteEphemeris}. */
+ public static final class Builder {
+ private int mSlotNumber;
+ private int mHealthState;
+ private double mFrameTimeSeconds;
+ private int mAgeInDays;
+ private GlonassSatelliteClockModel mSatelliteClockModel;
+ private GlonassSatelliteOrbitModel mSatelliteOrbitModel;
+
+ /**
+ * Sets the L1/Satellite system (R), satellite number (slot number in sat. constellation).
+ */
+ @NonNull
+ public Builder setSlotNumber(@IntRange(from = 1, to = 25) int slotNumber) {
+ mSlotNumber = slotNumber;
+ return this;
+ }
+
+ /** Sets the health state (0=healthy, 1=unhealthy). */
+ @NonNull
+ public Builder setHealthState(@IntRange(from = 0, to = 1) int healthState) {
+ mHealthState = healthState;
+ return this;
+ }
+
+ /** Sets the message frame time in seconds of the UTC week (tk+nd*86400). */
+ @NonNull
+ public Builder setFrameTimeSeconds(@FloatRange(from = 0.0f) double frameTimeSeconds) {
+ mFrameTimeSeconds = frameTimeSeconds;
+ return this;
+ }
+
+ /** Sets the age of current information in days (E). */
+ @NonNull
+ public Builder setAgeInDays(@IntRange(from = 0, to = 31) int ageInDays) {
+ mAgeInDays = ageInDays;
+ return this;
+ }
+
+ /** Sets the satellite clock model. */
+ @NonNull
+ public Builder setSatelliteClockModel(
+ @NonNull GlonassSatelliteClockModel satelliteClockModel) {
+ mSatelliteClockModel = satelliteClockModel;
+ return this;
+ }
+
+ /** Sets the satellite orbit model. */
+ @NonNull
+ public Builder setSatelliteOrbitModel(
+ @NonNull GlonassSatelliteOrbitModel satelliteOrbitModel) {
+ mSatelliteOrbitModel = satelliteOrbitModel;
+ return this;
+ }
+
+ /** Builds a {@link GlonassSatelliteEphemeris}. */
+ @NonNull
+ public GlonassSatelliteEphemeris build() {
+ return new GlonassSatelliteEphemeris(this);
+ }
+ }
+
+ /**
+ * A class contains the set of parameters needed for Glonass satellite clock correction.
+ *
+ * <p>This is defined in RINEX 3.05 APPENDIX 10 and Glonass ICD v5.1 section 4.4.
+ */
+ public static final class GlonassSatelliteClockModel implements Parcelable {
+ /**
+ * Time of the clock in seconds (UTC)
+ *
+ * <p>Corresponds to the 'Epoch' field within the 'SV/EPOCH/SV CLK' record of Glonass
+ * navigation message in RINEX 3.05 Table A10.
+ */
+ private final long mTimeOfClockSeconds;
+
+ /** Clock bias in seconds (-TauN). */
+ private final double mClockBias;
+
+ /** Frequency bias (+GammaN). */
+ private final double mFrequencyBias;
+
+ /** Frequency number. */
+ private final int mFrequencyNumber;
+
+ private GlonassSatelliteClockModel(Builder builder) {
+ Preconditions.checkArgument(builder.mTimeOfClockSeconds >= 0);
+ Preconditions.checkArgumentInRange(builder.mClockBias, -0.002f, 0.002f, "ClockBias");
+ Preconditions.checkArgumentInRange(
+ builder.mFrequencyBias, -9.32e-10f, 9.32e-10f, "FrequencyBias");
+ Preconditions.checkArgumentInRange(builder.mFrequencyNumber, -7, 6, "FrequencyNumber");
+ mTimeOfClockSeconds = builder.mTimeOfClockSeconds;
+ mClockBias = builder.mClockBias;
+ mFrequencyBias = builder.mFrequencyBias;
+ mFrequencyNumber = builder.mFrequencyNumber;
+ }
+
+ /** Returns the time of clock in seconds (UTC). */
+ @IntRange(from = 0)
+ public long getTimeOfClockSeconds() {
+ return mTimeOfClockSeconds;
+ }
+
+ /** Returns the clock bias in seconds (-TauN). */
+ @FloatRange(from = -0.002f, to = 0.002f)
+ public double getClockBias() {
+ return mClockBias;
+ }
+
+ /** Returns the frequency bias (+GammaN). */
+ @FloatRange(from = -9.32e-10f, to = 9.32e-10f)
+ public double getFrequencyBias() {
+ return mFrequencyBias;
+ }
+
+ /** Returns the frequency number. */
+ @IntRange(from = -7, to = 6)
+ public int getFrequencyNumber() {
+ return mFrequencyNumber;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mTimeOfClockSeconds);
+ dest.writeDouble(mClockBias);
+ dest.writeDouble(mFrequencyBias);
+ dest.writeInt(mFrequencyNumber);
+ }
+
+ public static final @NonNull Parcelable.Creator<GlonassSatelliteClockModel> CREATOR =
+ new Parcelable.Creator<GlonassSatelliteClockModel>() {
+ @Override
+ public GlonassSatelliteClockModel createFromParcel(@NonNull Parcel source) {
+ return new GlonassSatelliteClockModel.Builder()
+ .setTimeOfClockSeconds(source.readLong())
+ .setClockBias(source.readDouble())
+ .setFrequencyBias(source.readDouble())
+ .setFrequencyNumber(source.readInt())
+ .build();
+ }
+
+ @Override
+ public GlonassSatelliteClockModel[] newArray(int size) {
+ return new GlonassSatelliteClockModel[size];
+ }
+ };
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GlonassSatelliteClockModel[");
+ builder.append("timeOfClockSeconds = ").append(mTimeOfClockSeconds);
+ builder.append(", clockBias = ").append(mClockBias);
+ builder.append(", frequencyBias = ").append(mFrequencyBias);
+ builder.append(", frequencyNumber = ").append(mFrequencyNumber);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GlonassSatelliteClockModel}. */
+ public static final class Builder {
+ private long mTimeOfClockSeconds;
+ private double mClockBias;
+ private double mFrequencyBias;
+ private int mFrequencyNumber;
+
+ /** Sets the time of clock in seconds (UTC). */
+ @NonNull
+ public Builder setTimeOfClockSeconds(@IntRange(from = 0) long timeOfClockSeconds) {
+ mTimeOfClockSeconds = timeOfClockSeconds;
+ return this;
+ }
+
+ /** Sets the clock bias in seconds (-TauN). */
+ @NonNull
+ public Builder setClockBias(@FloatRange(from = -0.002f, to = 0.002f) double clockBias) {
+ mClockBias = clockBias;
+ return this;
+ }
+
+ /** Sets the frequency bias (+GammaN). */
+ @NonNull
+ public Builder setFrequencyBias(
+ @FloatRange(from = -9.32e-10f, to = 9.32e-10f) double frequencyBias) {
+ mFrequencyBias = frequencyBias;
+ return this;
+ }
+
+ /** Sets the frequency number. */
+ @NonNull
+ public Builder setFrequencyNumber(@IntRange(from = -7, to = 6) int frequencyNumber) {
+ mFrequencyNumber = frequencyNumber;
+ return this;
+ }
+
+ /** Builds a {@link GlonassSatelliteClockModel}. */
+ @NonNull
+ public GlonassSatelliteClockModel build() {
+ return new GlonassSatelliteClockModel(this);
+ }
+ }
+ }
+
+ /**
+ * A class contains the set of parameters needed for Glonass satellite orbit correction.
+ *
+ * <p>This is defined in RINEX 3.05 APPENDIX 10 and Glonass ICD v5.1 section 4.4.
+ */
+ public static final class GlonassSatelliteOrbitModel implements Parcelable {
+ /** X position in kilometers. */
+ private final double mX;
+
+ /** X velocity in kilometers per second. */
+ private final double mXDot;
+
+ /** X acceleration in kilometers per second squared. */
+ private final double mXAccel;
+
+ /** Y position in kilometers. */
+ private final double mY;
+
+ /** Y velocity in kilometers per second. */
+ private final double mYDot;
+
+ /** Y acceleration in kilometers per second squared. */
+ private final double mYAccel;
+
+ /** Z position in kilometers. */
+ private final double mZ;
+
+ /** Z velocity in kilometers per second. */
+ private final double mZDot;
+
+ /** Z acceleration in kilometers per second squared. */
+ private final double mZAccel;
+
+ private GlonassSatelliteOrbitModel(Builder builder) {
+ Preconditions.checkArgumentInRange(builder.mX, -2.7e4f, 2.7e4f, "X");
+ Preconditions.checkArgumentInRange(builder.mXDot, -4.3f, 4.3f, "XDot");
+ Preconditions.checkArgumentInRange(builder.mXAccel, -6.2e-9f, 6.2e-9f, "XAccel");
+ Preconditions.checkArgumentInRange(builder.mY, -2.7e4f, 2.7e4f, "Y");
+ Preconditions.checkArgumentInRange(builder.mYDot, -4.3f, 4.3f, "YDot");
+ Preconditions.checkArgumentInRange(builder.mYAccel, -6.2e-9f, 6.2e-9f, "YAccel");
+ Preconditions.checkArgumentInRange(builder.mZ, -2.7e4f, 2.7e4f, "Z");
+ Preconditions.checkArgumentInRange(builder.mZDot, -4.3f, 4.3f, "ZDot");
+ Preconditions.checkArgumentInRange(builder.mZAccel, -6.2e-9f, 6.2e-9f, "ZAccel");
+ mX = builder.mX;
+ mXDot = builder.mXDot;
+ mXAccel = builder.mXAccel;
+ mY = builder.mY;
+ mYDot = builder.mYDot;
+ mYAccel = builder.mYAccel;
+ mZ = builder.mZ;
+ mZDot = builder.mZDot;
+ mZAccel = builder.mZAccel;
+ }
+
+ /** Returns the X position in kilometers. */
+ @FloatRange(from = -2.7e4f, to = 2.7e4f)
+ public double getX() {
+ return mX;
+ }
+
+ /** Returns the X velocity in kilometers per second. */
+ @FloatRange(from = -4.3f, to = 4.3f)
+ public double getXDot() {
+ return mXDot;
+ }
+
+ /** Returns the X acceleration in kilometers per second squared. */
+ @FloatRange(from = -6.2e-9f, to = 6.2e-9f)
+ public double getXAccel() {
+ return mXAccel;
+ }
+
+ /** Returns the Y position in kilometers. */
+ @FloatRange(from = -2.7e4f, to = 2.7e4f)
+ public double getY() {
+ return mY;
+ }
+
+ /** Returns the Y velocity in kilometers per second. */
+ @FloatRange(from = -4.3f, to = 4.3f)
+ public double getYDot() {
+ return mYDot;
+ }
+
+ /** Returns the Y acceleration in kilometers per second squared. */
+ @FloatRange(from = -6.2e-9f, to = 6.2e-9f)
+ public double getYAccel() {
+ return mYAccel;
+ }
+
+ /** Returns the Z position in kilometers. */
+ @FloatRange(from = -2.7e4f, to = 2.7e4f)
+ public double getZ() {
+ return mZ;
+ }
+
+ /** Returns the Z velocity in kilometers per second. */
+ @FloatRange(from = -4.3f, to = 4.3f)
+ public double getZDot() {
+ return mZDot;
+ }
+
+ /** Returns the Z acceleration in kilometers per second squared. */
+ @FloatRange(from = -6.2e-9f, to = 6.2e-9f)
+ public double getZAccel() {
+ return mZAccel;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeDouble(mX);
+ dest.writeDouble(mXDot);
+ dest.writeDouble(mXAccel);
+ dest.writeDouble(mY);
+ dest.writeDouble(mYDot);
+ dest.writeDouble(mYAccel);
+ dest.writeDouble(mZ);
+ dest.writeDouble(mZDot);
+ dest.writeDouble(mZAccel);
+ }
+
+ public static final @NonNull Parcelable.Creator<GlonassSatelliteOrbitModel> CREATOR =
+ new Parcelable.Creator<GlonassSatelliteOrbitModel>() {
+ @Override
+ public GlonassSatelliteOrbitModel createFromParcel(@NonNull Parcel source) {
+ return new GlonassSatelliteOrbitModel.Builder()
+ .setX(source.readDouble())
+ .setXDot(source.readDouble())
+ .setXAccel(source.readDouble())
+ .setY(source.readDouble())
+ .setYDot(source.readDouble())
+ .setYAccel(source.readDouble())
+ .setZ(source.readDouble())
+ .setZDot(source.readDouble())
+ .setZAccel(source.readDouble())
+ .build();
+ }
+
+ @Override
+ public GlonassSatelliteOrbitModel[] newArray(int size) {
+ return new GlonassSatelliteOrbitModel[size];
+ }
+ };
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GlonassSatelliteOrbitModel[");
+ builder.append("x = ").append(mX);
+ builder.append(", xDot = ").append(mXDot);
+ builder.append(", xAccel = ").append(mXAccel);
+ builder.append(", y = ").append(mY);
+ builder.append(", yDot = ").append(mYDot);
+ builder.append(", yAccel = ").append(mYAccel);
+ builder.append(", z = ").append(mZ);
+ builder.append(", zDot = ").append(mZDot);
+ builder.append(", zAccel = ").append(mZAccel);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GlonassSatelliteOrbitModel}. */
+ public static final class Builder {
+ private double mX;
+ private double mXDot;
+ private double mXAccel;
+ private double mY;
+ private double mYDot;
+ private double mYAccel;
+ private double mZ;
+ private double mZDot;
+ private double mZAccel;
+
+ /** Sets the X position in kilometers. */
+ @NonNull
+ public Builder setX(@FloatRange(from = -2.7e4f, to = 2.7e4f) double x) {
+ mX = x;
+ return this;
+ }
+
+ /** Sets the X velocity in kilometers per second. */
+ @NonNull
+ public Builder setXDot(@FloatRange(from = -4.3f, to = 4.3f) double xDot) {
+ mXDot = xDot;
+ return this;
+ }
+
+ /** Sets the X acceleration in kilometers per second squared. */
+ @NonNull
+ public Builder setXAccel(@FloatRange(from = -6.2e-9f, to = 6.2e-9f) double xAccel) {
+ mXAccel = xAccel;
+ return this;
+ }
+
+ /** Sets the Y position in kilometers. */
+ @NonNull
+ public Builder setY(@FloatRange(from = -2.7e4f, to = 2.7e4f) double y) {
+ mY = y;
+ return this;
+ }
+
+ /** Sets the Y velocity in kilometers per second. */
+ @NonNull
+ public Builder setYDot(@FloatRange(from = -4.3f, to = 4.3f) double yDot) {
+ mYDot = yDot;
+ return this;
+ }
+
+ /** Sets the Y acceleration in kilometers per second squared. */
+ @NonNull
+ public Builder setYAccel(@FloatRange(from = -6.2e-9f, to = 6.2e-9f) double yAccel) {
+ mYAccel = yAccel;
+ return this;
+ }
+
+ /** Sets the Z position in kilometers. */
+ @NonNull
+ public Builder setZ(@FloatRange(from = -2.7e4f, to = 2.7e4f) double z) {
+ mZ = z;
+ return this;
+ }
+
+ /** Sets the Z velocity in kilometers per second. */
+ @NonNull
+ public Builder setZDot(@FloatRange(from = -4.3f, to = 4.3f) double zDot) {
+ mZDot = zDot;
+ return this;
+ }
+
+ /** Sets the Z acceleration in kilometers per second squared. */
+ @NonNull
+ public Builder setZAccel(@FloatRange(from = -6.2e-9f, to = 6.2e-9f) double zAccel) {
+ mZAccel = zAccel;
+ return this;
+ }
+
+ /** Builds a {@link GlonassSatelliteOrbitModel}. */
+ @NonNull
+ public GlonassSatelliteOrbitModel build() {
+ return new GlonassSatelliteOrbitModel(this);
+ }
+ }
+ }
+}
diff --git a/location/java/android/location/GnssAlmanac.java b/location/java/android/location/GnssAlmanac.java
new file mode 100644
index 0000000..6466e45a
--- /dev/null
+++ b/location/java/android/location/GnssAlmanac.java
@@ -0,0 +1,619 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class contains almanac parameters for GPS, QZSS, Galileo, Beidou.
+ *
+ * <p>For Beidou, this is defined in BDS-SIS-ICD-B1I-3.0 section 5.2.4.15.
+ *
+ * <p>For GPS, this is defined in IS-GPS-200 section 20.3.3.5.1.2.
+ *
+ * <p>For QZSS, this is defined in IS-QZSS-PNT section 4.1.2.6.
+ *
+ * <p>For Galileo, this is defined in Galileo-OS-SIS-ICD-v2.1 section 5.1.10.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GnssAlmanac implements Parcelable {
+ /**
+ * Almanac issue date in milliseconds (UTC).
+ *
+ * <p>This is unused for GPS/QZSS/Baidou.
+ */
+ private final long mIssueDateMillis;
+
+ /**
+ * Almanac issue of data.
+ *
+ * <p>This is unused for GPS/QZSS/Baidou.
+ */
+ private final int mIod;
+
+ /**
+ * Almanac reference week number.
+ *
+ * <p>For GPS and QZSS, this is GPS week number (modulo 1024).
+ *
+ * <p>For Beidou, this is Baidou week number (modulo 8192).
+ *
+ * <p>For Galileo, this is modulo 4 representation of the Galileo week number.
+ */
+ private final int mWeekNumber;
+
+ /** Almanac reference time in seconds. */
+ private final int mToaSeconds;
+
+ /** The list of GnssSatelliteAlmanacs. */
+ @NonNull private final List<GnssSatelliteAlmanac> mGnssSatelliteAlmanacs;
+
+ private GnssAlmanac(Builder builder) {
+ Preconditions.checkArgument(builder.mIssueDateMillis >= 0);
+ Preconditions.checkArgument(builder.mIod >= 0);
+ Preconditions.checkArgument(builder.mWeekNumber >= 0);
+ Preconditions.checkArgumentInRange(builder.mToaSeconds, 0, 604800, "ToaSeconds");
+ Preconditions.checkNotNull(
+ builder.mGnssSatelliteAlmanacs, "GnssSatelliteAlmanacs cannot be null");
+ mIssueDateMillis = builder.mIssueDateMillis;
+ mIod = builder.mIod;
+ mWeekNumber = builder.mWeekNumber;
+ mToaSeconds = builder.mToaSeconds;
+ mGnssSatelliteAlmanacs =
+ Collections.unmodifiableList(new ArrayList<>(builder.mGnssSatelliteAlmanacs));
+ }
+
+ /** Returns the almanac issue date in milliseconds (UTC). */
+ @IntRange(from = 0)
+ public long getIssueDateMillis() {
+ return mIssueDateMillis;
+ }
+
+ /** Returns the almanac issue of data. */
+ @IntRange(from = 0)
+ public int getIod() {
+ return mIod;
+ }
+
+ /**
+ * Returns the almanac reference week number.
+ *
+ * <p>For GPS and QZSS, this is GPS week number (modulo 1024).
+ *
+ * <p>For Beidou, this is Baidou week number (modulo 8192).
+ *
+ * <p>For Galileo, this is modulo 4 representation of the Galileo week number.
+ */
+ @IntRange(from = 0)
+ public int getWeekNumber() {
+ return mWeekNumber;
+ }
+
+ /** Returns the almanac reference time in seconds. */
+ @IntRange(from = 0, to = 604800)
+ public int getToaSeconds() {
+ return mToaSeconds;
+ }
+
+ /** Returns the list of GnssSatelliteAlmanacs. */
+ @NonNull
+ public List<GnssSatelliteAlmanac> getGnssSatelliteAlmanacs() {
+ return mGnssSatelliteAlmanacs;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mIssueDateMillis);
+ dest.writeInt(mIod);
+ dest.writeInt(mWeekNumber);
+ dest.writeInt(mToaSeconds);
+ dest.writeTypedList(mGnssSatelliteAlmanacs);
+ }
+
+ public static final @NonNull Creator<GnssAlmanac> CREATOR =
+ new Creator<GnssAlmanac>() {
+ @Override
+ public GnssAlmanac createFromParcel(Parcel in) {
+ GnssAlmanac.Builder gnssAlmanac = new GnssAlmanac.Builder();
+ gnssAlmanac.setIssueDateMillis(in.readLong());
+ gnssAlmanac.setIod(in.readInt());
+ gnssAlmanac.setWeekNumber(in.readInt());
+ gnssAlmanac.setToaSeconds(in.readInt());
+ List<GnssSatelliteAlmanac> satelliteAlmanacs = new ArrayList<>();
+ in.readTypedList(satelliteAlmanacs, GnssSatelliteAlmanac.CREATOR);
+ gnssAlmanac.setGnssSatelliteAlmanacs(satelliteAlmanacs);
+ return gnssAlmanac.build();
+ }
+
+ @Override
+ public GnssAlmanac[] newArray(int size) {
+ return new GnssAlmanac[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GnssAlmanac[");
+ builder.append("issueDateMillis=").append(mIssueDateMillis);
+ builder.append(", iod=").append(mIod);
+ builder.append(", weekNumber=").append(mWeekNumber);
+ builder.append(", toaSeconds=").append(mToaSeconds);
+ builder.append(", satelliteAlmanacs=").append(mGnssSatelliteAlmanacs);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GnssAlmanac}. */
+ public static final class Builder {
+ private long mIssueDateMillis;
+ private int mIod;
+ private int mWeekNumber;
+ private int mToaSeconds;
+ private List<GnssSatelliteAlmanac> mGnssSatelliteAlmanacs;
+
+ /** Sets the almanac issue date in milliseconds (UTC). */
+ @NonNull
+ public Builder setIssueDateMillis(@IntRange(from = 0) long issueDateMillis) {
+ mIssueDateMillis = issueDateMillis;
+ return this;
+ }
+
+ /** Sets the almanac issue of data. */
+ @NonNull
+ public Builder setIod(@IntRange(from = 0) int iod) {
+ mIod = iod;
+ return this;
+ }
+
+ /**
+ * Sets the almanac reference week number.
+ *
+ * <p>For GPS and QZSS, this is GPS week number (modulo 1024).
+ *
+ * <p>For Beidou, this is Baidou week number (modulo 8192).
+ *
+ * <p>For Galileo, this is modulo 4 representation of the Galileo week number.
+ */
+ @NonNull
+ public Builder setWeekNumber(@IntRange(from = 0) int weekNumber) {
+ mWeekNumber = weekNumber;
+ return this;
+ }
+
+ /** Sets the almanac reference time in seconds. */
+ @NonNull
+ public Builder setToaSeconds(@IntRange(from = 0, to = 604800) int toaSeconds) {
+ mToaSeconds = toaSeconds;
+ return this;
+ }
+
+ /** Sets the list of GnssSatelliteAlmanacs. */
+ @NonNull
+ public Builder setGnssSatelliteAlmanacs(
+ @NonNull List<GnssSatelliteAlmanac> gnssSatelliteAlmanacs) {
+ mGnssSatelliteAlmanacs = gnssSatelliteAlmanacs;
+ return this;
+ }
+
+ /** Builds a {@link GnssAlmanac} instance as specified by this builder. */
+ @NonNull
+ public GnssAlmanac build() {
+ return new GnssAlmanac(this);
+ }
+ }
+
+ /**
+ * A class contains almanac parameters for GPS, QZSS, Galileo, Beidou.
+ *
+ * <p>For Beidou, this is defined in BDS-SIS-ICD-B1I-3.0 section 5.2.4.15.
+ *
+ * <p>For GPS, this is defined in IS-GPS-200 section 20.3.3.5.1.2.
+ *
+ * <p>For QZSS, this is defined in IS-QZSS-PNT section 4.1.2.6.
+ *
+ * <p>For Galileo, this is defined in Galileo-OS-SIS-ICD-v2.1 section 5.1.10.
+ */
+ public static final class GnssSatelliteAlmanac implements Parcelable {
+ /** The PRN number of the GNSS satellite. */
+ private final int mSvid;
+
+ /**
+ * Satellite health information.
+ *
+ * <p>For GPS, this is satellite subframe 4 and 5, page 25 6-bit health code as defined in
+ * IS-GPS-200 Table 20-VIII expressed in integer form.
+ *
+ * <p>For QZSS, this is the 5-bit health code as defined in IS-QZSS-PNT, Table 4.1.2-5-2
+ * expressed in integer form.
+ *
+ * <p>For Beidou, this is 1-bit health information. (0=healthy, 1=unhealthy).
+ *
+ * <p>For Galileo, this is 6-bit health, bit 0 and 1 is for E5a, bit 2 and 3 is for E5b, bit
+ * 4 and 5 is for E1b.
+ */
+ private final int mSvHealth;
+
+ /** Eccentricity. */
+ private final double mEccentricity;
+
+ /**
+ * Inclination in semi-circles.
+ *
+ * <p>For GPS and Galileo, this is the difference between the inclination angle at reference
+ * time and the nominal inclination in semi-circles.
+ *
+ * <p>For Beidou and QZSS, this is the inclination angle at reference time in semi-circles.
+ */
+ private final double mInclination;
+
+ /** Argument of perigee in semi-circles. */
+ private final double mOmega;
+
+ /** Longitude of ascending node of orbital plane at weekly epoch in semi-circles. */
+ private final double mOmega0;
+
+ /** Rate of right ascension in semi-circles per second. */
+ private final double mOmegaDot;
+
+ /**
+ * Square root of semi-major axis in square root of meters.
+ *
+ * <p>For Galileo, this is the difference with respect to the square root of the nominal
+ * semi-major axis in square root of meters.
+ */
+ private final double mRootA;
+
+ /** Mean anomaly at reference time in semi-circles. */
+ private final double mM0;
+
+ /** Satellite clock time bias correction coefficient in seconds. */
+ private final double mAf0;
+
+ /** Satellite clock time drift correction coefficient in seconds per second. */
+ private final double mAf1;
+
+ private GnssSatelliteAlmanac(Builder builder) {
+ Preconditions.checkArgument(builder.mSvid > 0);
+ Preconditions.checkArgument(builder.mSvHealth >= 0);
+ Preconditions.checkArgument(builder.mEccentricity >= 0.0f);
+ Preconditions.checkArgumentInRange(builder.mInclination, -1.0f, 1.0f, "Inclination");
+ Preconditions.checkArgumentInRange(builder.mOmega, -1.0f, 1.0f, "Omega");
+ Preconditions.checkArgumentInRange(builder.mOmega0, -1.0f, 1.0f, "Omega0");
+ Preconditions.checkArgumentInRange(builder.mOmegaDot, -1.0f, 1.0f, "OmegaDot");
+ Preconditions.checkArgumentInRange(builder.mRootA, 0.0f, 8192.0f, "RootA");
+ Preconditions.checkArgumentInRange(builder.mM0, -1.0f, 1.0f, "M0");
+ Preconditions.checkArgumentInRange(builder.mAf0, -0.0625f, 0.0625f, "Af0");
+ Preconditions.checkArgumentInRange(builder.mAf1, -1.5e-8f, 1.5e-8f, "Af1");
+ mSvid = builder.mSvid;
+ mSvHealth = builder.mSvHealth;
+ mEccentricity = builder.mEccentricity;
+ mInclination = builder.mInclination;
+ mOmega = builder.mOmega;
+ mOmega0 = builder.mOmega0;
+ mOmegaDot = builder.mOmegaDot;
+ mRootA = builder.mRootA;
+ mM0 = builder.mM0;
+ mAf0 = builder.mAf0;
+ mAf1 = builder.mAf1;
+ }
+
+ /** Returns the PRN number of the GNSS satellite. */
+ @IntRange(from = 1)
+ public int getSvid() {
+ return mSvid;
+ }
+
+ /**
+ * Returns the satellite health information.
+ *
+ * <p>For GPS, this is satellite subframe 4 and 5, page 25 6-bit health code as defined in
+ * IS-GPS-200 Table 20-VIII expressed in integer form.
+ *
+ * <p>For QZSS, this is the 5-bit health code as defined in IS-QZSS-PNT, Table 4.1.2-5-2
+ * expressed in integer form.
+ *
+ * <p>For Beidou, this is 1-bit health information. (0=healthy, 1=unhealthy).
+ *
+ * <p>For Galileo, this is 6-bit health, bit 0 and 1 is for E5a, bit 2 and 3 is for E5b,
+ * bit 4 and 5 is for E1b.
+ */
+ @IntRange(from = 0)
+ public int getSvHealth() {
+ return mSvHealth;
+ }
+
+ /** Returns the eccentricity. */
+ @FloatRange(from = 0.0f)
+ public double getEccentricity() {
+ return mEccentricity;
+ }
+
+ /**
+ * Returns the inclination in semi-circles.
+ *
+ * <p>For GPS and Galileo, this is the difference between the inclination angle at reference
+ * time and the nominal inclination in semi-circles.
+ *
+ * <p>For Beidou and QZSS, this is the inclination angle at reference time in semi-circles.
+ */
+ @FloatRange(from = -1.0f, to = 1.0f)
+ public double getInclination() {
+ return mInclination;
+ }
+
+ /** Returns the argument of perigee in semi-circles. */
+ @FloatRange(from = -1.0f, to = 1.0f)
+ public double getOmega() {
+ return mOmega;
+ }
+
+ /**
+ * Returns the longitude of ascending node of orbital plane at weekly epoch in semi-circles.
+ */
+ @FloatRange(from = -1.0f, to = 1.0f)
+ public double getOmega0() {
+ return mOmega0;
+ }
+
+ /** Returns the rate of right ascension in semi-circles per second. */
+ @FloatRange(from = -1.0f, to = 1.0f)
+ public double getOmegaDot() {
+ return mOmegaDot;
+ }
+
+ /**
+ * Returns the square root of semi-major axis in square root of meters.
+ *
+ * <p>For Galileo, this is the difference with respect to the square root of the nominal
+ * semi-major axis in square root of meters.
+ */
+ @FloatRange(from = 0.0f, to = 8192.0f)
+ public double getRootA() {
+ return mRootA;
+ }
+
+ /** Returns the mean anomaly at reference time in semi-circles. */
+ @FloatRange(from = -1.0f, to = 1.0f)
+ public double getM0() {
+ return mM0;
+ }
+
+ /** Returns the satellite clock time bias correction coefficient in seconds. */
+ @FloatRange(from = -0.0625f, to = 0.0625f)
+ public double getAf0() {
+ return mAf0;
+ }
+
+ /** Returns the satellite clock time drift correction coefficient in seconds per second. */
+ @FloatRange(from = -1.5e-8f, to = 1.5e-8f)
+ public double getAf1() {
+ return mAf1;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mSvid);
+ dest.writeInt(mSvHealth);
+ dest.writeDouble(mEccentricity);
+ dest.writeDouble(mInclination);
+ dest.writeDouble(mOmega);
+ dest.writeDouble(mOmega0);
+ dest.writeDouble(mOmegaDot);
+ dest.writeDouble(mRootA);
+ dest.writeDouble(mM0);
+ dest.writeDouble(mAf0);
+ dest.writeDouble(mAf1);
+ }
+
+ public static final @NonNull Creator<GnssSatelliteAlmanac> CREATOR =
+ new Creator<GnssSatelliteAlmanac>() {
+ @Override
+ public GnssSatelliteAlmanac createFromParcel(Parcel in) {
+ return new GnssSatelliteAlmanac(
+ new Builder()
+ .setSvid(in.readInt())
+ .setSvHealth(in.readInt())
+ .setEccentricity(in.readDouble())
+ .setInclination(in.readDouble())
+ .setOmega(in.readDouble())
+ .setOmega0(in.readDouble())
+ .setOmegaDot(in.readDouble())
+ .setRootA(in.readDouble())
+ .setM0(in.readDouble())
+ .setAf0(in.readDouble())
+ .setAf1(in.readDouble()));
+ }
+
+ @Override
+ public GnssSatelliteAlmanac[] newArray(int size) {
+ return new GnssSatelliteAlmanac[size];
+ }
+ };
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GnssSatelliteAlmanac[");
+ builder.append("svid = ").append(mSvid);
+ builder.append(", svHealth = ").append(mSvHealth);
+ builder.append(", eccentricity = ").append(mEccentricity);
+ builder.append(", inclination = ").append(mInclination);
+ builder.append(", omega = ").append(mOmega);
+ builder.append(", omega0 = ").append(mOmega0);
+ builder.append(", omegaDot = ").append(mOmegaDot);
+ builder.append(", rootA = ").append(mRootA);
+ builder.append(", m0 = ").append(mM0);
+ builder.append(", af0 = ").append(mAf0);
+ builder.append(", af1 = ").append(mAf1);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GnssSatelliteAlmanac}. */
+ public static final class Builder {
+ private int mSvid;
+ private int mSvHealth;
+ private double mEccentricity;
+ private double mInclination;
+ private double mOmega;
+ private double mOmega0;
+ private double mOmegaDot;
+ private double mRootA;
+ private double mM0;
+ private double mAf0;
+ private double mAf1;
+
+ /** Sets the PRN number of the GNSS satellite. */
+ @NonNull
+ public Builder setSvid(@IntRange(from = 1) int svid) {
+ mSvid = svid;
+ return this;
+ }
+
+ /**
+ * Sets the satellite health information.
+ *
+ * <p>For GPS, this is satellite subframe 4 and 5, page 25 6-bit health code as defined
+ * in IS-GPS-200 Table 20-VIII expressed in integer form.
+ *
+ * <p>For QZSS, this is the 5-bit health code as defined in IS-QZSS-PNT, Table 4.1.2-5-2
+ * expressed in integer form.
+ *
+ * <p>For Beidou, this is 1-bit health information. (0=healthy, 1=unhealthy).
+ *
+ * <p>For Galileo, this is 6-bit health, bit 0 and 1 is for E5a,
+ * bit 2 and 3 is for E5b, bit 4 and 5 is for E1b.
+ */
+ @NonNull
+ public Builder setSvHealth(@IntRange(from = 0) int svHealth) {
+ mSvHealth = svHealth;
+ return this;
+ }
+
+ /** Sets the eccentricity. */
+ @NonNull
+ public Builder setEccentricity(@FloatRange(from = 0.0f) double eccentricity) {
+ mEccentricity = eccentricity;
+ return this;
+ }
+
+ /**
+ * Sets the inclination in semi-circles.
+ *
+ * <p>For GPS and Galileo, this is the difference between the inclination angle at
+ * reference time and the nominal inclination in semi-circles.
+ *
+ * <p>For Beidou and QZSS, this is the inclination angle at reference time in
+ * semi-circles.
+ */
+ @NonNull
+ public Builder setInclination(@FloatRange(from = -1.0f, to = 1.0f) double inclination) {
+ mInclination = inclination;
+ return this;
+ }
+
+ /** Sets the argument of perigee in semi-circles. */
+ @NonNull
+ public Builder setOmega(@FloatRange(from = -1.0f, to = 1.0f) double omega) {
+ mOmega = omega;
+ return this;
+ }
+
+ /**
+ * Sets the longitude of ascending node of orbital plane at weekly epoch in
+ * semi-circles.
+ */
+ @NonNull
+ public Builder setOmega0(@FloatRange(from = -1.0f, to = 1.0f) double omega0) {
+ mOmega0 = omega0;
+ return this;
+ }
+
+ /** Sets the rate of right ascension in semi-circles per second. */
+ @NonNull
+ public Builder setOmegaDot(@FloatRange(from = -1.0f, to = 1.0f) double omegaDot) {
+ mOmegaDot = omegaDot;
+ return this;
+ }
+
+ /**
+ * Sets the square root of semi-major axis in square root of meters.
+ *
+ * <p>For Galileo, this is the difference with respect to the square root of the nominal
+ * semi-major axis in square root of meters.
+ */
+ @NonNull
+ public Builder setRootA(@FloatRange(from = 0.0f, to = 8192.0f) double rootA) {
+ mRootA = rootA;
+ return this;
+ }
+
+ /** Sets the mean anomaly at reference time in semi-circles. */
+ @NonNull
+ public Builder setM0(@FloatRange(from = -1.0f, to = 1.0f) double m0) {
+ mM0 = m0;
+ return this;
+ }
+
+ /** Sets the satellite clock time bias correction coefficient in seconds. */
+ @NonNull
+ public Builder setAf0(@FloatRange(from = -0.0625f, to = 0.0625f) double af0) {
+ mAf0 = af0;
+ return this;
+ }
+
+ /** Sets the satellite clock time drift correction coefficient in seconds per second. */
+ @NonNull
+ public Builder setAf1(@FloatRange(from = -1.5e-8f, to = 1.5e-8f) double af1) {
+ mAf1 = af1;
+ return this;
+ }
+
+ /** Builds a {@link GnssSatelliteAlmanac} instance as specified by this builder. */
+ @NonNull
+ public GnssSatelliteAlmanac build() {
+ return new GnssSatelliteAlmanac(this);
+ }
+ }
+ }
+}
diff --git a/core/java/android/app/ondeviceintelligence/Feature.aidl b/location/java/android/location/GnssAssistance.aidl
similarity index 80%
copy from core/java/android/app/ondeviceintelligence/Feature.aidl
copy to location/java/android/location/GnssAssistance.aidl
index 18494d7..2745683 100644
--- a/core/java/android/app/ondeviceintelligence/Feature.aidl
+++ b/location/java/android/location/GnssAssistance.aidl
@@ -5,7 +5,7 @@
* you may not use this 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,
@@ -13,10 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.location;
-package android.app.ondeviceintelligence;
-
-/**
- * @hide
- */
-parcelable Feature;
+parcelable GnssAssistance;
\ No newline at end of file
diff --git a/location/java/android/location/GnssAssistance.java b/location/java/android/location/GnssAssistance.java
new file mode 100644
index 0000000..e941122
--- /dev/null
+++ b/location/java/android/location/GnssAssistance.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class contains GNSS assistance.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GnssAssistance implements Parcelable {
+
+ /** GPS assistance. */
+ @Nullable private final GpsAssistance mGpsAssistance;
+
+ /** Glonass assistance. */
+ @Nullable private final GlonassAssistance mGlonassAssistance;
+
+ /** Galileo assistance. */
+ @Nullable private final GalileoAssistance mGalileoAssistance;
+
+ /** Beidou assistance. */
+ @Nullable private final BeidouAssistance mBeidouAssistance;
+
+ /** QZSS assistance. */
+ @Nullable private final QzssAssistance mQzssAssistance;
+
+ private GnssAssistance(Builder builder) {
+ mGpsAssistance = builder.mGpsAssistance;
+ mGlonassAssistance = builder.mGlonassAssistance;
+ mGalileoAssistance = builder.mGalileoAssistance;
+ mBeidouAssistance = builder.mBeidouAssistance;
+ mQzssAssistance = builder.mQzssAssistance;
+ }
+
+ /** Returns the GPS assistance. */
+ @Nullable
+ public GpsAssistance getGpsAssistance() {
+ return mGpsAssistance;
+ }
+
+ /** Returns the Glonass assistance. */
+ @Nullable
+ public GlonassAssistance getGlonassAssistance() {
+ return mGlonassAssistance;
+ }
+
+ /** Returns the Galileo assistance. */
+ @Nullable
+ public GalileoAssistance getGalileoAssistance() {
+ return mGalileoAssistance;
+ }
+
+ /** Returns the Beidou assistance. */
+ @Nullable
+ public BeidouAssistance getBeidouAssistance() {
+ return mBeidouAssistance;
+ }
+
+ /** Returns the Qzss assistance. */
+ @Nullable
+ public QzssAssistance getQzssAssistance() {
+ return mQzssAssistance;
+ }
+
+ public static final @NonNull Creator<GnssAssistance> CREATOR =
+ new Creator<GnssAssistance>() {
+ @Override
+ @NonNull
+ public GnssAssistance createFromParcel(Parcel in) {
+ return new GnssAssistance.Builder()
+ .setGpsAssistance(in.readTypedObject(GpsAssistance.CREATOR))
+ .setGlonassAssistance(in.readTypedObject(GlonassAssistance.CREATOR))
+ .setGalileoAssistance(in.readTypedObject(GalileoAssistance.CREATOR))
+ .setBeidouAssistance(in.readTypedObject(BeidouAssistance.CREATOR))
+ .setQzssAssistance(in.readTypedObject(QzssAssistance.CREATOR))
+ .build();
+ }
+
+ @Override
+ public GnssAssistance[] newArray(int size) {
+ return new GnssAssistance[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeTypedObject(mGpsAssistance, flags);
+ parcel.writeTypedObject(mGlonassAssistance, flags);
+ parcel.writeTypedObject(mGalileoAssistance, flags);
+ parcel.writeTypedObject(mBeidouAssistance, flags);
+ parcel.writeTypedObject(mQzssAssistance, flags);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GnssAssistance[");
+ builder.append("gpsAssistance = ").append(mGpsAssistance);
+ builder.append(", glonassAssistance = ").append(mGlonassAssistance);
+ builder.append(", galileoAssistance = ").append(mGalileoAssistance);
+ builder.append(", beidouAssistance = ").append(mBeidouAssistance);
+ builder.append(", qzssAssistance = ").append(mQzssAssistance);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GnssAssistance}. */
+ public static final class Builder {
+ private GpsAssistance mGpsAssistance;
+ private GlonassAssistance mGlonassAssistance;
+ private GalileoAssistance mGalileoAssistance;
+ private BeidouAssistance mBeidouAssistance;
+ private QzssAssistance mQzssAssistance;
+
+ /** Sets the GPS assistance. */
+ @NonNull
+ public Builder setGpsAssistance(@Nullable GpsAssistance gpsAssistance) {
+ mGpsAssistance = gpsAssistance;
+ return this;
+ }
+
+ /** Sets the Glonass assistance. */
+ @NonNull
+ public Builder setGlonassAssistance(@Nullable GlonassAssistance glonassAssistance) {
+ mGlonassAssistance = glonassAssistance;
+ return this;
+ }
+
+ /** Sets the Galileo assistance. */
+ @NonNull
+ public Builder setGalileoAssistance(@Nullable GalileoAssistance galileoAssistance) {
+ mGalileoAssistance = galileoAssistance;
+ return this;
+ }
+
+ /** Sets the Beidou assistance. */
+ @NonNull
+ public Builder setBeidouAssistance(@Nullable BeidouAssistance beidouAssistance) {
+ mBeidouAssistance = beidouAssistance;
+ return this;
+ }
+
+ /** Sets the QZSS assistance. */
+ @NonNull
+ public Builder setQzssAssistance(@Nullable QzssAssistance qzssAssistance) {
+ mQzssAssistance = qzssAssistance;
+ return this;
+ }
+
+ /** Builds a {@link GnssAssistance} instance as specified by this builder. */
+ @NonNull
+ public GnssAssistance build() {
+ return new GnssAssistance(this);
+ }
+ }
+
+ /** A class contains GNSS corrections for satellites. */
+ public static final class GnssSatelliteCorrections implements Parcelable {
+ /**
+ * Pseudo-random or satellite ID number for the satellite, a.k.a. Space Vehicle (SV), or OSN
+ * number for Glonass.
+ *
+ * <p>The distinction is made by looking at the constellation field. Values must be in the
+ * range of:
+ *
+ * <p>- GPS: 1-32
+ *
+ * <p>- GLONASS: 1-25
+ *
+ * <p>- QZSS: 183-206
+ *
+ * <p>- Galileo: 1-36
+ *
+ * <p>- Beidou: 1-63
+ */
+ @IntRange(from = 1, to = 206)
+ int mSvid;
+
+ /** List of Ionospheric corrections */
+ @NonNull List<IonosphericCorrection> mIonosphericCorrections;
+
+ /**
+ * Creates a new {@link GnssSatelliteCorrections} instance.
+ *
+ * @param svid The Pseudo-random or satellite ID number for the satellite, a.k.a. Space
+ * Vehicle (SV), or OSN number for Glonass.
+ * <p>The distinction is made by looking at the constellation field. Values must be in
+ * the range of:
+ * <p>- GPS: 1-32
+ * <p>- GLONASS: 1-25
+ * <p>- QZSS: 183-206
+ * <p>- Galileo: 1-36
+ * <p>- Beidou: 1-63
+ * @param ionosphericCorrections The list of Ionospheric corrections.
+ */
+ public GnssSatelliteCorrections(
+ @IntRange(from = 1, to = 206) int svid,
+ @NonNull final List<IonosphericCorrection> ionosphericCorrections) {
+ // Allow SV ID beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(svid >= 1);
+ Preconditions.checkNotNull(
+ ionosphericCorrections, "IonosphericCorrections cannot be null");
+ mSvid = svid;
+ mIonosphericCorrections =
+ Collections.unmodifiableList(new ArrayList<>(ionosphericCorrections));
+ }
+
+ /**
+ * Returns the Pseudo-random or satellite ID number for the satellite, a.k.a. Space Vehicle
+ * (SV), or OSN number for Glonass.
+ *
+ * <p>The distinction is made by looking at the constellation field. Values must be in the
+ * range of:
+ *
+ * <p>- GPS: 1-32
+ *
+ * <p>- GLONASS: 1-25
+ *
+ * <p>- QZSS: 183-206
+ *
+ * <p>- Galileo: 1-36
+ *
+ * <p>- Beidou: 1-63
+ */
+ @IntRange(from = 1, to = 206)
+ public int getSvid() {
+ return mSvid;
+ }
+
+ /** Returns the list of Ionospheric corrections. */
+ @NonNull
+ public List<IonosphericCorrection> getIonosphericCorrections() {
+ return mIonosphericCorrections;
+ }
+
+ public static final @NonNull Creator<GnssSatelliteCorrections> CREATOR =
+ new Creator<GnssSatelliteCorrections>() {
+ @Override
+ @NonNull
+ public GnssSatelliteCorrections createFromParcel(Parcel in) {
+ int svid = in.readInt();
+ List<IonosphericCorrection> ionosphericCorrections = new ArrayList<>();
+ in.readTypedList(ionosphericCorrections, IonosphericCorrection.CREATOR);
+ return new GnssSatelliteCorrections(svid, ionosphericCorrections);
+ }
+
+ @Override
+ public GnssSatelliteCorrections[] newArray(int size) {
+ return new GnssSatelliteCorrections[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mSvid);
+ parcel.writeTypedList(mIonosphericCorrections, flags);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GnssSatelliteCorrections[");
+ builder.append("svid = ").append(mSvid);
+ builder.append(", ionosphericCorrections = ").append(mIonosphericCorrections);
+ builder.append("]");
+ return builder.toString();
+ }
+ }
+}
diff --git a/location/java/android/location/GnssCorrectionComponent.java b/location/java/android/location/GnssCorrectionComponent.java
new file mode 100644
index 0000000..f55dde1
--- /dev/null
+++ b/location/java/android/location/GnssCorrectionComponent.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class that contains Gnss correction associated with a component (e.g. the Ionospheric error).
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GnssCorrectionComponent implements Parcelable {
+ /**
+ * Uniquely identifies the source of correction (e.g. "Klobuchar" for ionospheric corrections).
+ * Clients should not depend on the value of the source key but, rather, can compare
+ * before/after to detect changes.
+ */
+ @NonNull private final String mSourceKey;
+
+ /** The correction is only applicable during this time interval. */
+ @NonNull private final GnssInterval mValidityInterval;
+
+ /** Pseudorange correction. */
+ @NonNull private final PseudorangeCorrection mPseudorangeCorrection;
+
+ /**
+ * Creates a GnssCorrectionComponent.
+ *
+ * @param sourceKey Uniquely identifies the source of correction (e.g. "Klobuchar" for
+ * ionospheric corrections). Clients should not depend on the value of the source key but,
+ * rather, can compare before/after to detect changes.
+ * @param validityInterval The correction is only applicable during this time interval.
+ * @param pseudorangeCorrection Pseudorange correction.
+ */
+ public GnssCorrectionComponent(
+ @NonNull String sourceKey,
+ @NonNull GnssInterval validityInterval,
+ @NonNull PseudorangeCorrection pseudorangeCorrection) {
+ Preconditions.checkNotNull(sourceKey, "SourceKey cannot be null");
+ Preconditions.checkNotNull(validityInterval, "ValidityInterval cannot be null");
+ Preconditions.checkNotNull(pseudorangeCorrection, "PseudorangeCorrection cannot be null");
+ mSourceKey = sourceKey;
+ mValidityInterval = validityInterval;
+ mPseudorangeCorrection = pseudorangeCorrection;
+ }
+
+ /** Returns the source key of the correction. */
+ @NonNull
+ public String getSourceKey() {
+ return mSourceKey;
+ }
+
+ /** Returns the validity interval of the correction. */
+ @NonNull
+ public GnssInterval getValidityInterval() {
+ return mValidityInterval;
+ }
+
+ /** Returns the pseudorange correction. */
+ @NonNull
+ public PseudorangeCorrection getPseudorangeCorrection() {
+ return mPseudorangeCorrection;
+ }
+
+ public static final @NonNull Creator<GnssCorrectionComponent> CREATOR =
+ new Creator<GnssCorrectionComponent>() {
+ @Override
+ @NonNull
+ public GnssCorrectionComponent createFromParcel(Parcel in) {
+ String sourceKey = in.readString8();
+ GnssInterval validityInterval = in.readTypedObject(GnssInterval.CREATOR);
+ PseudorangeCorrection pseudorangeCorrection =
+ in.readTypedObject(PseudorangeCorrection.CREATOR);
+ return new GnssCorrectionComponent(
+ sourceKey, validityInterval, pseudorangeCorrection);
+ }
+
+ @Override
+ public GnssCorrectionComponent[] newArray(int size) {
+ return new GnssCorrectionComponent[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mSourceKey);
+ dest.writeTypedObject(mValidityInterval, flags);
+ dest.writeTypedObject(mPseudorangeCorrection, flags);
+ }
+
+ /**
+ * Time interval referenced against the GPS epoch. The start must be less than or equal to the
+ * end. When the start equals the end, the interval is empty.
+ */
+ public static final class GnssInterval implements Parcelable {
+ /**
+ * Inclusive start of the interval in milliseconds since the GPS epoch. A timestamp matching
+ * this interval will have to be the same or after the start. Required as a reference time
+ * for the initial correction value and its rate of change over time.
+ */
+ private final long mStartMillisSinceGpsEpoch;
+
+ /**
+ * Exclusive end of the interval in milliseconds since the GPS epoch. If specified, a
+ * timestamp matching this interval will have to be before the end.
+ */
+ private final long mEndMillisSinceGpsEpoch;
+
+ /**
+ * Creates a GnssInterval.
+ *
+ * @param startMillisSinceGpsEpoch Inclusive start of the interval in milliseconds since the
+ * GPS epoch. A timestamp matching this interval will have to be the same or after the
+ * start. Required as a reference time for the initial correction value and its rate of
+ * change over time.
+ * @param endMillisSinceGpsEpoch Exclusive end of the interval in milliseconds since the GPS
+ * epoch. If specified, a timestamp matching this interval will have to be before the
+ * end.
+ */
+ public GnssInterval(
+ @IntRange(from = 0) long startMillisSinceGpsEpoch,
+ @IntRange(from = 0) long endMillisSinceGpsEpoch) {
+ Preconditions.checkArgument(startMillisSinceGpsEpoch >= 0);
+ Preconditions.checkArgument(endMillisSinceGpsEpoch >= 0);
+ mStartMillisSinceGpsEpoch = startMillisSinceGpsEpoch;
+ mEndMillisSinceGpsEpoch = endMillisSinceGpsEpoch;
+ }
+
+ /** Returns the inclusive start of the interval in milliseconds since the GPS epoch. */
+ @IntRange(from = 0)
+ public long getStartMillisSinceGpsEpoch() {
+ return mStartMillisSinceGpsEpoch;
+ }
+
+ /** Returns the exclusive end of the interval in milliseconds since the GPS epoch. */
+ @IntRange(from = 0)
+ public long getEndMillisSinceGpsEpoch() {
+ return mEndMillisSinceGpsEpoch;
+ }
+
+ public static final @NonNull Creator<GnssInterval> CREATOR =
+ new Creator<GnssInterval>() {
+ @Override
+ @NonNull
+ public GnssInterval createFromParcel(Parcel in) {
+ return new GnssInterval(in.readLong(), in.readLong());
+ }
+
+ @Override
+ public GnssInterval[] newArray(int size) {
+ return new GnssInterval[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeLong(mStartMillisSinceGpsEpoch);
+ parcel.writeLong(mEndMillisSinceGpsEpoch);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GnssInterval[");
+ builder.append("startMillisSinceGpsEpoch = ").append(mStartMillisSinceGpsEpoch);
+ builder.append(", endMillisSinceGpsEpoch = ").append(mEndMillisSinceGpsEpoch);
+ builder.append("]");
+ return builder.toString();
+ }
+ }
+
+ /** Pseudorange correction. */
+ public static final class PseudorangeCorrection implements Parcelable {
+
+ /** Correction to be added to the measured pseudorange, in meters. */
+ private final double mCorrectionMeters;
+
+ /** Uncertainty of the correction, in meters. */
+ private final double mCorrectionUncertaintyMeters;
+
+ /**
+ * Linear approximation of the change in correction over time. Intended usage is to adjust
+ * the correction using the formula: correctionMeters + correctionRateMetersPerSecond *
+ * delta_seconds Where `delta_seconds` is the number of elapsed seconds since the beginning
+ * of the correction validity interval.
+ */
+ private final double mCorrectionRateMetersPerSecond;
+
+ /**
+ * Creates a PseudorangeCorrection.
+ *
+ * @param correctionMeters Correction to be added to the measured pseudorange, in meters.
+ * @param correctionUncertaintyMeters Uncertainty of the correction, in meters.
+ * @param correctionRateMetersPerSecond Linear approximation of the change in correction
+ * over time. Intended usage is to adjust the correction using the formula:
+ * correctionMeters + correctionRateMetersPerSecond * delta_seconds Where
+ * `delta_seconds` is the number of elapsed seconds since the beginning of the
+ * correction validity interval.
+ */
+ public PseudorangeCorrection(
+ double correctionMeters,
+ double correctionUncertaintyMeters,
+ double correctionRateMetersPerSecond) {
+ Preconditions.checkArgument(correctionUncertaintyMeters >= 0);
+ mCorrectionMeters = correctionMeters;
+ mCorrectionUncertaintyMeters = correctionUncertaintyMeters;
+ mCorrectionRateMetersPerSecond = correctionRateMetersPerSecond;
+ }
+
+ /** Returns the correction to be added to the measured pseudorange, in meters. */
+ public double getCorrectionMeters() {
+ return mCorrectionMeters;
+ }
+
+ /** Returns the uncertainty of the correction, in meters. */
+ @FloatRange(from = 0.0f)
+ public double getCorrectionUncertaintyMeters() {
+ return mCorrectionUncertaintyMeters;
+ }
+
+ /** Returns the linear approximation of the change in correction over time. */
+ public double getCorrectionRateMetersPerSecond() {
+ return mCorrectionRateMetersPerSecond;
+ }
+
+ public static final @NonNull Creator<PseudorangeCorrection> CREATOR =
+ new Creator<PseudorangeCorrection>() {
+ @Override
+ @NonNull
+ public PseudorangeCorrection createFromParcel(Parcel in) {
+ return new PseudorangeCorrection(
+ in.readDouble(), in.readDouble(), in.readDouble());
+ }
+
+ @Override
+ public PseudorangeCorrection[] newArray(int size) {
+ return new PseudorangeCorrection[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeDouble(mCorrectionMeters);
+ parcel.writeDouble(mCorrectionUncertaintyMeters);
+ parcel.writeDouble(mCorrectionRateMetersPerSecond);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("PseudorangeCorrection[");
+ builder.append("correctionMeters = ").append(mCorrectionMeters);
+ builder.append(", correctionUncertaintyMeters = ").append(mCorrectionUncertaintyMeters);
+ builder.append(", correctionRateMetersPerSecond = ")
+ .append(mCorrectionRateMetersPerSecond);
+ builder.append("]");
+ return builder.toString();
+ }
+ }
+}
diff --git a/location/java/android/location/GpsAssistance.java b/location/java/android/location/GpsAssistance.java
new file mode 100644
index 0000000..5202fc4
--- /dev/null
+++ b/location/java/android/location/GpsAssistance.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.location.GnssAssistance.GnssSatelliteCorrections;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class contains GPS assistance.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GpsAssistance implements Parcelable {
+
+ /** The GPS almanac. */
+ @Nullable private final GnssAlmanac mAlmanac;
+
+ /** The Klobuchar ionospheric model. */
+ @Nullable private final KlobucharIonosphericModel mIonosphericModel;
+
+ /** The UTC model. */
+ @Nullable private final UtcModel mUtcModel;
+
+ /** The leap seconds model. */
+ @Nullable private final LeapSecondsModel mLeapSecondsModel;
+
+ /** The list of time models. */
+ @NonNull private final List<TimeModel> mTimeModels;
+
+ /** The list of GPS ephemeris. */
+ @NonNull private final List<GpsSatelliteEphemeris> mSatelliteEphemeris;
+
+ /** The list of real time integrity models. */
+ @NonNull private final List<RealTimeIntegrityModel> mRealTimeIntegrityModels;
+
+ /** The list of GPS satellite corrections. */
+ @NonNull private final List<GnssSatelliteCorrections> mSatelliteCorrections;
+
+ private GpsAssistance(Builder builder) {
+ mAlmanac = builder.mAlmanac;
+ mIonosphericModel = builder.mIonosphericModel;
+ mUtcModel = builder.mUtcModel;
+ mLeapSecondsModel = builder.mLeapSecondsModel;
+ if (builder.mTimeModels != null) {
+ mTimeModels = Collections.unmodifiableList(new ArrayList<>(builder.mTimeModels));
+ } else {
+ mTimeModels = new ArrayList<>();
+ }
+ if (builder.mSatelliteEphemeris != null) {
+ mSatelliteEphemeris =
+ Collections.unmodifiableList(new ArrayList<>(builder.mSatelliteEphemeris));
+ } else {
+ mSatelliteEphemeris = new ArrayList<>();
+ }
+ if (builder.mRealTimeIntegrityModels != null) {
+ mRealTimeIntegrityModels =
+ Collections.unmodifiableList(new ArrayList<>(builder.mRealTimeIntegrityModels));
+ } else {
+ mRealTimeIntegrityModels = new ArrayList<>();
+ }
+ if (builder.mSatelliteCorrections != null) {
+ mSatelliteCorrections =
+ Collections.unmodifiableList(new ArrayList<>(builder.mSatelliteCorrections));
+ } else {
+ mSatelliteCorrections = new ArrayList<>();
+ }
+ }
+
+ /** Returns the GPS almanac. */
+ @Nullable
+ public GnssAlmanac getAlmanac() {
+ return mAlmanac;
+ }
+
+ /** Returns the Klobuchar ionospheric model. */
+ @Nullable
+ public KlobucharIonosphericModel getIonosphericModel() {
+ return mIonosphericModel;
+ }
+
+ /** Returns the UTC model. */
+ @Nullable
+ public UtcModel getUtcModel() {
+ return mUtcModel;
+ }
+
+ /** Returns the leap seconds model. */
+ @Nullable
+ public LeapSecondsModel getLeapSecondsModel() {
+ return mLeapSecondsModel;
+ }
+
+ /** Returns the list of time models. */
+ @NonNull
+ public List<TimeModel> getTimeModels() {
+ return mTimeModels;
+ }
+
+ /** Returns the list of GPS ephemeris. */
+ @NonNull
+ public List<GpsSatelliteEphemeris> getSatelliteEphemeris() {
+ return mSatelliteEphemeris;
+ }
+
+ /** Returns the list of real time integrity models. */
+ @NonNull
+ public List<RealTimeIntegrityModel> getRealTimeIntegrityModels() {
+ return mRealTimeIntegrityModels;
+ }
+
+ /** Returns the list of GPS satellite corrections. */
+ @NonNull
+ public List<GnssSatelliteCorrections> getSatelliteCorrections() {
+ return mSatelliteCorrections;
+ }
+
+ public static final @NonNull Creator<GpsAssistance> CREATOR =
+ new Creator<GpsAssistance>() {
+ @Override
+ @NonNull
+ public GpsAssistance createFromParcel(Parcel in) {
+ return new GpsAssistance.Builder()
+ .setAlmanac(in.readTypedObject(GnssAlmanac.CREATOR))
+ .setIonosphericModel(
+ in.readTypedObject(KlobucharIonosphericModel.CREATOR))
+ .setUtcModel(in.readTypedObject(UtcModel.CREATOR))
+ .setLeapSecondsModel(in.readTypedObject(LeapSecondsModel.CREATOR))
+ .setTimeModels(in.createTypedArrayList(TimeModel.CREATOR))
+ .setSatelliteEphemeris(
+ in.createTypedArrayList(GpsSatelliteEphemeris.CREATOR))
+ .setRealTimeIntegrityModels(
+ in.createTypedArrayList(RealTimeIntegrityModel.CREATOR))
+ .setSatelliteCorrections(
+ in.createTypedArrayList(GnssSatelliteCorrections.CREATOR))
+ .build();
+ }
+
+ @Override
+ public GpsAssistance[] newArray(int size) {
+ return new GpsAssistance[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(mAlmanac, flags);
+ dest.writeTypedObject(mIonosphericModel, flags);
+ dest.writeTypedObject(mUtcModel, flags);
+ dest.writeTypedObject(mLeapSecondsModel, flags);
+ dest.writeTypedList(mTimeModels);
+ dest.writeTypedList(mSatelliteEphemeris);
+ dest.writeTypedList(mRealTimeIntegrityModels);
+ dest.writeTypedList(mSatelliteCorrections);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GnssAssistance[");
+ builder.append("almanac = ").append(mAlmanac);
+ builder.append(", ionosphericModel = ").append(mIonosphericModel);
+ builder.append(", utcModel = ").append(mUtcModel);
+ builder.append(", leapSecondsModel = ").append(mLeapSecondsModel);
+ builder.append(", timeModels = ").append(mTimeModels);
+ builder.append(", satelliteEphemeris = ").append(mSatelliteEphemeris);
+ builder.append(", realTimeIntegrityModels = ").append(mRealTimeIntegrityModels);
+ builder.append(", satelliteCorrections = ").append(mSatelliteCorrections);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GpsAssistance}. */
+ public static final class Builder {
+ private GnssAlmanac mAlmanac;
+ private KlobucharIonosphericModel mIonosphericModel;
+ private UtcModel mUtcModel;
+ private LeapSecondsModel mLeapSecondsModel;
+ private List<TimeModel> mTimeModels;
+ private List<GpsSatelliteEphemeris> mSatelliteEphemeris;
+ private List<RealTimeIntegrityModel> mRealTimeIntegrityModels;
+ private List<GnssSatelliteCorrections> mSatelliteCorrections;
+
+ /** Sets the GPS almanac. */
+ @NonNull
+ public Builder setAlmanac(
+ @Nullable @SuppressLint("NullableCollection") GnssAlmanac almanac) {
+ mAlmanac = almanac;
+ return this;
+ }
+
+ /** Sets the Klobuchar ionospheric model. */
+ @NonNull
+ public Builder setIonosphericModel(
+ @Nullable @SuppressLint("NullableCollection")
+ KlobucharIonosphericModel ionosphericModel) {
+ mIonosphericModel = ionosphericModel;
+ return this;
+ }
+
+ /** Sets the UTC model. */
+ @NonNull
+ public Builder setUtcModel(
+ @Nullable @SuppressLint("NullableCollection") UtcModel utcModel) {
+ mUtcModel = utcModel;
+ return this;
+ }
+
+ /** Sets the leap seconds model. */
+ @NonNull
+ public Builder setLeapSecondsModel(
+ @Nullable @SuppressLint("NullableCollection") LeapSecondsModel leapSecondsModel) {
+ mLeapSecondsModel = leapSecondsModel;
+ return this;
+ }
+
+ /** Sets the list of time models. */
+ @NonNull
+ public Builder setTimeModels(
+ @Nullable @SuppressLint("NullableCollection") List<TimeModel> timeModels) {
+ mTimeModels = timeModels;
+ return this;
+ }
+
+ /** Sets the list of GPS ephemeris. */
+ @NonNull
+ public Builder setSatelliteEphemeris(
+ @Nullable @SuppressLint("NullableCollection")
+ List<GpsSatelliteEphemeris> satelliteEphemeris) {
+ mSatelliteEphemeris = satelliteEphemeris;
+ return this;
+ }
+
+ /** Sets the list of real time integrity models. */
+ @NonNull
+ public Builder setRealTimeIntegrityModels(
+ @Nullable @SuppressLint("NullableCollection")
+ List<RealTimeIntegrityModel> realTimeIntegrityModels) {
+ mRealTimeIntegrityModels = realTimeIntegrityModels;
+ return this;
+ }
+
+ /** Sets the list of GPS satellite corrections. */
+ @NonNull
+ public Builder setSatelliteCorrections(
+ @Nullable @SuppressLint("NullableCollection")
+ List<GnssSatelliteCorrections> satelliteCorrections) {
+ mSatelliteCorrections = satelliteCorrections;
+ return this;
+ }
+
+ /** Builds a {@link GpsAssistance} instance as specified by this builder. */
+ @NonNull
+ public GpsAssistance build() {
+ return new GpsAssistance(this);
+ }
+ }
+}
diff --git a/location/java/android/location/GpsSatelliteEphemeris.java b/location/java/android/location/GpsSatelliteEphemeris.java
new file mode 100644
index 0000000..ec6bc59
--- /dev/null
+++ b/location/java/android/location/GpsSatelliteEphemeris.java
@@ -0,0 +1,632 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * This class contains ephemeris parameters specific to GPS satellites.
+ *
+ * <p>This is defined in IS-GPS-200 section 20.3.3.3.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class GpsSatelliteEphemeris implements Parcelable {
+ /** Satellite PRN */
+ private final int mPrn;
+
+ /** L2 parameters. */
+ @NonNull private final GpsL2Params mGpsL2Params;
+
+ /** Clock model. */
+ @NonNull private final GpsSatelliteClockModel mSatelliteClockModel;
+
+ /** Orbit model. */
+ @NonNull private final KeplerianOrbitModel mSatelliteOrbitModel;
+
+ /** Satellite health. */
+ @NonNull private final GpsSatelliteHealth mSatelliteHealth;
+
+ /** Ephemeris time. */
+ @NonNull private final SatelliteEphemerisTime mSatelliteEphemerisTime;
+
+ private GpsSatelliteEphemeris(Builder builder) {
+ // Allow PRN beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mPrn >= 1);
+ Preconditions.checkNotNull(builder.mGpsL2Params, "GPSL2Params cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteClockModel,
+ "SatelliteClockModel cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteOrbitModel,
+ "SatelliteOrbitModel cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteHealth,
+ "SatelliteHealth cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteEphemerisTime,
+ "SatelliteEphemerisTime cannot be null");
+ mPrn = builder.mPrn;
+ mGpsL2Params = builder.mGpsL2Params;
+ mSatelliteClockModel = builder.mSatelliteClockModel;
+ mSatelliteOrbitModel = builder.mSatelliteOrbitModel;
+ mSatelliteHealth = builder.mSatelliteHealth;
+ mSatelliteEphemerisTime = builder.mSatelliteEphemerisTime;
+ }
+
+ /** Returns the PRN of the satellite. */
+ @IntRange(from = 1, to = 32)
+ public int getPrn() {
+ return mPrn;
+ }
+
+ /** Returns the L2 parameters of the satellite. */
+ @NonNull
+ public GpsL2Params getGpsL2Params() {
+ return mGpsL2Params;
+ }
+
+ /** Returns the clock model of the satellite. */
+ @NonNull
+ public GpsSatelliteClockModel getSatelliteClockModel() {
+ return mSatelliteClockModel;
+ }
+
+ /** Returns the orbit model of the satellite. */
+ @NonNull
+ public KeplerianOrbitModel getSatelliteOrbitModel() {
+ return mSatelliteOrbitModel;
+ }
+
+ /** Returns the satellite health. */
+ @NonNull
+ public GpsSatelliteHealth getSatelliteHealth() {
+ return mSatelliteHealth;
+ }
+
+ /** Returns the ephemeris time. */
+ @NonNull
+ public SatelliteEphemerisTime getSatelliteEphemerisTime() {
+ return mSatelliteEphemerisTime;
+ }
+
+ public static final @NonNull Creator<GpsSatelliteEphemeris> CREATOR =
+ new Creator<GpsSatelliteEphemeris>() {
+ @Override
+ @NonNull
+ public GpsSatelliteEphemeris createFromParcel(Parcel in) {
+ final GpsSatelliteEphemeris.Builder gpsSatelliteEphemeris =
+ new Builder()
+ .setPrn(in.readInt())
+ .setGpsL2Params(in.readTypedObject(GpsL2Params.CREATOR))
+ .setSatelliteClockModel(
+ in.readTypedObject(GpsSatelliteClockModel.CREATOR))
+ .setSatelliteOrbitModel(
+ in.readTypedObject(KeplerianOrbitModel.CREATOR))
+ .setSatelliteHealth(
+ in.readTypedObject(GpsSatelliteHealth.CREATOR))
+ .setSatelliteEphemerisTime(
+ in.readTypedObject(SatelliteEphemerisTime.CREATOR));
+ return gpsSatelliteEphemeris.build();
+ }
+
+ @Override
+ public GpsSatelliteEphemeris[] newArray(int size) {
+ return new GpsSatelliteEphemeris[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mPrn);
+ parcel.writeTypedObject(mGpsL2Params, flags);
+ parcel.writeTypedObject(mSatelliteClockModel, flags);
+ parcel.writeTypedObject(mSatelliteOrbitModel, flags);
+ parcel.writeTypedObject(mSatelliteHealth, flags);
+ parcel.writeTypedObject(mSatelliteEphemerisTime, flags);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GpsSatelliteEphemeris[");
+ builder.append("prn = ").append(mPrn);
+ builder.append(", gpsL2Params = ").append(mGpsL2Params);
+ builder.append(", satelliteClockModel = ").append(mSatelliteClockModel);
+ builder.append(", satelliteOrbitModel = ").append(mSatelliteOrbitModel);
+ builder.append(", satelliteHealth = ").append(mSatelliteHealth);
+ builder.append(", satelliteEphemerisTime = ").append(mSatelliteEphemerisTime);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GpsSatelliteEphemeris} */
+ public static final class Builder {
+ private int mPrn = 0;
+ private GpsL2Params mGpsL2Params;
+ private GpsSatelliteClockModel mSatelliteClockModel;
+ private KeplerianOrbitModel mSatelliteOrbitModel;
+ private GpsSatelliteHealth mSatelliteHealth;
+ private SatelliteEphemerisTime mSatelliteEphemerisTime;
+
+ /** Sets the PRN of the satellite. */
+ @NonNull
+ public Builder setPrn(@IntRange(from = 1, to = 32) int prn) {
+ mPrn = prn;
+ return this;
+ }
+
+ /** Sets the L2 parameters of the satellite. */
+ @NonNull
+ public Builder setGpsL2Params(@NonNull GpsL2Params gpsL2Params) {
+ mGpsL2Params = gpsL2Params;
+ return this;
+ }
+
+ /** Sets the clock model of the satellite. */
+ @NonNull
+ public Builder setSatelliteClockModel(@NonNull GpsSatelliteClockModel satelliteClockModel) {
+ mSatelliteClockModel = satelliteClockModel;
+ return this;
+ }
+
+ /** Sets the orbit model of the satellite. */
+ @NonNull
+ public Builder setSatelliteOrbitModel(@NonNull KeplerianOrbitModel satelliteOrbitModel) {
+ mSatelliteOrbitModel = satelliteOrbitModel;
+ return this;
+ }
+
+ /** Sets the satellite health. */
+ @NonNull
+ public Builder setSatelliteHealth(@NonNull GpsSatelliteHealth satelliteHealth) {
+ mSatelliteHealth = satelliteHealth;
+ return this;
+ }
+
+ /** Sets the ephemeris time. */
+ @NonNull
+ public Builder setSatelliteEphemerisTime(
+ @NonNull SatelliteEphemerisTime satelliteEphemerisTime) {
+ mSatelliteEphemerisTime = satelliteEphemerisTime;
+ return this;
+ }
+
+ /** Builds a {@link GpsSatelliteEphemeris} instance as specified by this builder. */
+ @NonNull
+ public GpsSatelliteEphemeris build() {
+ return new GpsSatelliteEphemeris(this);
+ }
+ }
+
+ /**
+ * A class contains information about GPS health. The information is tied to Legacy Navigation
+ * (LNAV) data, not Civil Navigation (CNAV) data.
+ */
+ public static final class GpsSatelliteHealth implements Parcelable {
+ /**
+ * Represents "SV health" in the "BROADCAST ORBIT - 6" record of RINEX 3.05. Table A6,
+ * pp.68.
+ */
+ private final int mSvHealth;
+
+ /**
+ * Represents "SV accuracy" in meters in the "BROADCAST ORBIT - 6" record of RINEX 3.05.
+ * Table A6, pp.68.
+ */
+ private final double mSvAccur;
+
+ /**
+ * Represents the "Fit Interval" in hours in the "BROADCAST ORBIT - 7" record of RINEX 3.05.
+ * Table A6, pp.69.
+ */
+ private final double mFitInt;
+
+ /** Returns the SV health. */
+ @IntRange(from = 0, to = 63)
+ public int getSvHealth() {
+ return mSvHealth;
+ }
+
+ /** Returns the SV accuracy in meters. */
+ @FloatRange(from = 0.0f, to = 8192.0f)
+ public double getSvAccur() {
+ return mSvAccur;
+ }
+
+ /** Returns the fit interval in hours. */
+ @FloatRange(from = 0.0f)
+ public double getFitInt() {
+ return mFitInt;
+ }
+
+ private GpsSatelliteHealth(Builder builder) {
+ // Allow SV health beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mSvHealth >= 0);
+ Preconditions.checkArgumentInRange(builder.mSvAccur, 0.0f, 8192.0f, "SvAccur");
+ Preconditions.checkArgument(builder.mFitInt >= 0.0f);
+ mSvHealth = builder.mSvHealth;
+ mSvAccur = builder.mSvAccur;
+ mFitInt = builder.mFitInt;
+ }
+
+ public static final @NonNull Creator<GpsSatelliteHealth> CREATOR =
+ new Creator<GpsSatelliteHealth>() {
+ @Override
+ @NonNull
+ public GpsSatelliteHealth createFromParcel(Parcel in) {
+ final GpsSatelliteHealth.Builder gpsSatelliteHealth =
+ new Builder()
+ .setSvHealth(in.readInt())
+ .setSvAccur(in.readDouble())
+ .setFitInt(in.readDouble());
+ return gpsSatelliteHealth.build();
+ }
+
+ @Override
+ public GpsSatelliteHealth[] newArray(int size) {
+ return new GpsSatelliteHealth[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mSvHealth);
+ parcel.writeDouble(mSvAccur);
+ parcel.writeDouble(mFitInt);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GpsSatelliteHealth[");
+ builder.append("svHealth = ").append(mSvHealth);
+ builder.append(", svAccur = ").append(mSvAccur);
+ builder.append(", fitInt = ").append(mFitInt);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GpsSatelliteHealth}. */
+ public static final class Builder {
+ private int mSvHealth;
+ private double mSvAccur;
+ private double mFitInt;
+
+ /** Sets the SV health. */
+ @NonNull
+ public Builder setSvHealth(@IntRange(from = 0, to = 63) int svHealth) {
+ mSvHealth = svHealth;
+ return this;
+ }
+
+ /** Sets the SV accuracy in meters. */
+ @NonNull
+ public Builder setSvAccur(@FloatRange(from = 0.0f, to = 8192.0f) double svAccur) {
+ mSvAccur = svAccur;
+ return this;
+ }
+
+ /** Sets the fit interval in hours. */
+ @NonNull
+ public Builder setFitInt(@FloatRange(from = 0.0f) double fitInt) {
+ mFitInt = fitInt;
+ return this;
+ }
+
+ /** Builds a {@link GpsSatelliteHealth} instance as specified by this builder. */
+ @NonNull
+ public GpsSatelliteHealth build() {
+ return new GpsSatelliteHealth(this);
+ }
+ }
+ }
+
+ /** A class contains L2 parameters specific to GPS satellites. */
+ public static final class GpsL2Params implements Parcelable {
+ /** Code(s) on L2 Channel. */
+ private final int mL2Code;
+
+ /** Data Flag for L2 P-Code. */
+ private final int mL2Flag;
+
+ /** Returns the code(s) on L2 channel. */
+ @IntRange(from = 0, to = 3)
+ public int getL2Code() {
+ return mL2Code;
+ }
+
+ /** Returns the data flag for L2 P-code. */
+ @IntRange(from = 0, to = 1)
+ public int getL2Flag() {
+ return mL2Flag;
+ }
+
+ private GpsL2Params(Builder builder) {
+ Preconditions.checkArgumentInRange(builder.mL2Code, 0, 3, "L2 code");
+ Preconditions.checkArgumentInRange(builder.mL2Flag, 0, 1, "L2 flag");
+ mL2Code = builder.mL2Code;
+ mL2Flag = builder.mL2Flag;
+ }
+
+ public static final @NonNull Creator<GpsL2Params> CREATOR =
+ new Creator<GpsL2Params>() {
+ @Override
+ @NonNull
+ public GpsL2Params createFromParcel(Parcel in) {
+ final GpsL2Params.Builder gpsL2Params =
+ new Builder().setL2Code(in.readInt()).setL2Flag(in.readInt());
+ return gpsL2Params.build();
+ }
+
+ @Override
+ public GpsL2Params[] newArray(int size) {
+ return new GpsL2Params[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mL2Code);
+ parcel.writeInt(mL2Flag);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GpsL2Params[");
+ builder.append("l2Code = ").append(mL2Code);
+ builder.append(", l2Flag = ").append(mL2Flag);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GpsL2Params}. */
+ public static final class Builder {
+ private int mL2Code = 0;
+ private int mL2Flag = 0;
+
+ /** Sets the code(s) on L2 channel. */
+ @NonNull
+ public Builder setL2Code(@IntRange(from = 0, to = 3) int l2Code) {
+ mL2Code = l2Code;
+ return this;
+ }
+
+ /** Sets the data flag for L2 P-code. */
+ @NonNull
+ public Builder setL2Flag(@IntRange(from = 0, to = 1) int l2Flag) {
+ mL2Flag = l2Flag;
+ return this;
+ }
+
+ /** Builds a {@link GpsL2Params} instance as specified by this builder. */
+ @NonNull
+ public GpsL2Params build() {
+ return new GpsL2Params(this);
+ }
+ }
+ }
+
+ /** A class contains the set of parameters needed for GPS satellite clock correction. */
+ public static final class GpsSatelliteClockModel implements Parcelable {
+ /**
+ * Time of the clock in seconds since GPS epoch.
+ *
+ * <p>Corresponds to the 'Epoch' field within the 'SV/EPOCH/SV CLK' record of GPS
+ * navigation message in RINEX 3.05 Table A6.
+ */
+ private final long mTimeOfClockSeconds;
+
+ /** SV clock bias in seconds. */
+ private final double mAf0;
+
+ /** SV clock drift in seconds per second. */
+ private final double mAf1;
+
+ /** Clock drift rate in seconds per second squared. */
+ private final double mAf2;
+
+ /** Group delay differential in seconds. */
+ private final double mTgd;
+
+ /** Issue of data, clock. */
+ private final int mIodc;
+
+ private GpsSatelliteClockModel(Builder builder) {
+ Preconditions.checkArgument(builder.mTimeOfClockSeconds >= 0);
+ Preconditions.checkArgumentInRange(builder.mAf0, -9.77e-3f, 9.77e-3f, "Af0");
+ Preconditions.checkArgumentInRange(builder.mAf1, -3.73e-9f, 3.73e-9f, "Af1");
+ Preconditions.checkArgumentInRange(builder.mAf2, -3.56e-15f, 3.56e-15f, "Af2");
+ Preconditions.checkArgumentInRange(builder.mTgd, -5.97e-8f, 5.97e-8f, "Tgd");
+ Preconditions.checkArgumentInRange(builder.mIodc, 0, 1023, "Iodc");
+ mTimeOfClockSeconds = builder.mTimeOfClockSeconds;
+ mAf0 = builder.mAf0;
+ mAf1 = builder.mAf1;
+ mAf2 = builder.mAf2;
+ mTgd = builder.mTgd;
+ mIodc = builder.mIodc;
+ }
+
+ /** Returns the time of the clock in seconds since GPS epoch. */
+ @IntRange(from = 0)
+ public long getTimeOfClockSeconds() {
+ return mTimeOfClockSeconds;
+ }
+
+ /** Returns the SV clock bias in seconds. */
+ @FloatRange(from = -9.77e-3f, to = 9.77e-3f)
+ public double getAf0() {
+ return mAf0;
+ }
+
+ /** Returns the SV clock drift in seconds per second. */
+ @FloatRange(from = -3.73e-9f, to = 3.73e-9f)
+ public double getAf1() {
+ return mAf1;
+ }
+
+ /** Returns the clock drift rate in seconds per second squared. */
+ @FloatRange(from = -3.56e-15f, to = 3.56e-15f)
+ public double getAf2() {
+ return mAf2;
+ }
+
+ /** Returns the group delay differential in seconds. */
+ @FloatRange(from = -5.97e-8f, to = 5.97e-8f)
+ public double getTgd() {
+ return mTgd;
+ }
+
+ /** Returns the issue of data, clock. */
+ @IntRange(from = 0, to = 1023)
+ public int getIodc() {
+ return mIodc;
+ }
+
+ public static final @NonNull Creator<GpsSatelliteClockModel> CREATOR =
+ new Creator<GpsSatelliteClockModel>() {
+ @Override
+ @NonNull
+ public GpsSatelliteClockModel createFromParcel(Parcel in) {
+ final GpsSatelliteClockModel.Builder gpsSatelliteClockModel =
+ new Builder()
+ .setTimeOfClockSeconds(in.readLong())
+ .setAf0(in.readDouble())
+ .setAf1(in.readDouble())
+ .setAf2(in.readDouble())
+ .setTgd(in.readDouble())
+ .setIodc(in.readInt());
+ return gpsSatelliteClockModel.build();
+ }
+
+ @Override
+ public GpsSatelliteClockModel[] newArray(int size) {
+ return new GpsSatelliteClockModel[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeLong(mTimeOfClockSeconds);
+ parcel.writeDouble(mAf0);
+ parcel.writeDouble(mAf1);
+ parcel.writeDouble(mAf2);
+ parcel.writeDouble(mTgd);
+ parcel.writeInt(mIodc);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("GpsSatelliteClockModel[");
+ builder.append("timeOfClockSeconds = ").append(mTimeOfClockSeconds);
+ builder.append(", af0 = ").append(mAf0);
+ builder.append(", af1 = ").append(mAf1);
+ builder.append(", af2 = ").append(mAf2);
+ builder.append(", tgd = ").append(mTgd);
+ builder.append(", iodc = ").append(mIodc);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link GpsSatelliteClockModel}. */
+ public static final class Builder {
+ private long mTimeOfClockSeconds;
+ private double mAf0;
+ private double mAf1;
+ private double mAf2;
+ private double mTgd;
+ private int mIodc;
+
+ /** Sets the time of the clock in seconds since GPS epoch. */
+ @NonNull
+ public Builder setTimeOfClockSeconds(@IntRange(from = 0) long timeOfClockSeconds) {
+ mTimeOfClockSeconds = timeOfClockSeconds;
+ return this;
+ }
+
+ /** Sets the SV clock bias in seconds. */
+ @NonNull
+ public Builder setAf0(@FloatRange(from = -9.77e-3f, to = 9.77e-3f) double af0) {
+ mAf0 = af0;
+ return this;
+ }
+
+ /** Sets the SV clock drift in seconds per second. */
+ @NonNull
+ public Builder setAf1(@FloatRange(from = -3.73e-9f, to = 3.73e-9f) double af1) {
+ mAf1 = af1;
+ return this;
+ }
+
+ /** Sets the clock drift rate in seconds per second squared. */
+ @NonNull
+ public Builder setAf2(@FloatRange(from = -3.56e-15f, to = 3.56e-15f) double af2) {
+ mAf2 = af2;
+ return this;
+ }
+
+ /** Sets the group delay differential in seconds. */
+ @NonNull
+ public Builder setTgd(@FloatRange(from = -5.97e-8f, to = 5.97e-8f) double tgd) {
+ mTgd = tgd;
+ return this;
+ }
+
+ /** Sets the issue of data, clock. */
+ @NonNull
+ public Builder setIodc(@IntRange(from = 0, to = 1023) int iodc) {
+ mIodc = iodc;
+ return this;
+ }
+
+ /** Builds a {@link GpsSatelliteClockModel} instance as specified by this builder. */
+ @NonNull
+ public GpsSatelliteClockModel build() {
+ return new GpsSatelliteClockModel(this);
+ }
+ }
+ }
+}
diff --git a/location/java/android/location/IonosphericCorrection.java b/location/java/android/location/IonosphericCorrection.java
new file mode 100644
index 0000000..aafcf83
--- /dev/null
+++ b/location/java/android/location/IonosphericCorrection.java
@@ -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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class contains ionospheric correction.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class IonosphericCorrection implements Parcelable {
+
+ /** Carrier frequency in Hz to differentiate signals from the same satellite. e.g. GPS L1/L5 */
+ private final long mCarrierFrequencyHz;
+
+ /** Ionospheric correction. */
+ @NonNull private final GnssCorrectionComponent mIonosphericCorrection;
+
+ /**
+ * Creates a new {@link IonosphericCorrection} instance.
+ *
+ * @param carrierFrequencyHz Carrier frequency in Hz to differentiate signals from the
+ * samesatellite. e.g. GPS L1/L5
+ * @param ionosphericCorrection Ionospheric correction.
+ */
+ public IonosphericCorrection(
+ @IntRange(from = 0) long carrierFrequencyHz,
+ @NonNull GnssCorrectionComponent ionosphericCorrection) {
+ Preconditions.checkArgument(carrierFrequencyHz > 0);
+ Preconditions.checkNotNull(ionosphericCorrection, "IonosphericCorrection cannot be null");
+ mCarrierFrequencyHz = carrierFrequencyHz;
+ mIonosphericCorrection = ionosphericCorrection;
+ }
+
+ /**
+ * Returns the carrier frequency in Hz to differentiate signals from the same satellite. e.g.
+ * GPS L1/L5
+ */
+ @IntRange(from = 0)
+ public long getCarrierFrequencyHz() {
+ return mCarrierFrequencyHz;
+ }
+
+ /** Returns the Ionospheric correction. */
+ @NonNull
+ public GnssCorrectionComponent getIonosphericCorrection() {
+ return mIonosphericCorrection;
+ }
+
+ public static final @NonNull Creator<IonosphericCorrection> CREATOR =
+ new Creator<IonosphericCorrection>() {
+ @Override
+ @NonNull
+ public IonosphericCorrection createFromParcel(Parcel in) {
+ long carrierFrequencyHz = in.readLong();
+ GnssCorrectionComponent ionosphericCorrection =
+ in.readTypedObject(GnssCorrectionComponent.CREATOR);
+ return new IonosphericCorrection(carrierFrequencyHz, ionosphericCorrection);
+ }
+
+ @Override
+ public IonosphericCorrection[] newArray(int size) {
+ return new IonosphericCorrection[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mCarrierFrequencyHz);
+ dest.writeTypedObject(mIonosphericCorrection, flags);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("IonosphericCorrection[");
+ builder.append("carrierFrequencyHz = ").append(mCarrierFrequencyHz);
+ builder.append(", ionosphericCorrection = ").append(mIonosphericCorrection);
+ builder.append("]");
+ return builder.toString();
+ }
+}
diff --git a/location/java/android/location/KeplerianOrbitModel.java b/location/java/android/location/KeplerianOrbitModel.java
new file mode 100644
index 0000000..a118274
--- /dev/null
+++ b/location/java/android/location/KeplerianOrbitModel.java
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Contains Keplerian orbit model parameters for GPS/Galileo/QZSS/Beidou.
+ * <p>For GPS, this is defined in IS-GPS-200 Table 20-II.
+ * <p>For Galileo, this is defined in Galileo-OS-SIS-ICD-v2.1 section 5.1.1.
+ * <p>For QZSS, this is defined in IS-QZSS-PNT section 4.1.2.
+ * <p>For Baidou, this is defined in BDS-SIS-ICD-B1I-3.0 section 5.2.4.12.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class KeplerianOrbitModel implements Parcelable {
+ /** Square root of the semi-major axis in square root of meters. */
+ private final double mRootA;
+
+ /** Eccentricity. */
+ private final double mEccentricity;
+
+ /** Inclination angle at reference time in radians. */
+ private final double mI0;
+
+ /** Rate of change of inclination angle in radians per second. */
+ private final double mIDot;
+
+ /** Argument of perigee in radians. */
+ private final double mOmega;
+
+ /** Longitude of ascending node of orbit plane at beginning of week in radians. */
+ private final double mOmega0;
+
+ /** Rate of right ascension in radians per second. */
+ private final double mOmegaDot;
+
+ /** Mean anomaly at reference time in radians. */
+ private final double mM0;
+
+ /** Mean motion difference from computed value in radians per second. */
+ private final double mDeltaN;
+
+ /** Second-order harmonic perturbations. */
+ SecondOrderHarmonicPerturbation mSecondOrderHarmonicPerturbation;
+
+ private KeplerianOrbitModel(Builder builder) {
+ Preconditions.checkArgumentInRange(builder.mRootA, 0.0f, 8192.0f, "RootA");
+ Preconditions.checkArgumentInRange(builder.mEccentricity, 0.0f, 0.5f, "Eccentricity");
+ Preconditions.checkArgumentInRange(builder.mI0, -3.15f, 3.15f, "I0");
+ Preconditions.checkArgumentInRange(builder.mIDot, -2.94e-9f, 2.94e-9f, "IDot");
+ Preconditions.checkArgumentInRange(builder.mOmega, -3.15f, 3.15f, "Omega");
+ Preconditions.checkArgumentInRange(builder.mOmega0, -3.15f, 3.15f, "Omega0");
+ Preconditions.checkArgumentInRange(builder.mOmegaDot, -3.1e-6f, 3.1e-6f, "OmegaDot");
+ Preconditions.checkArgumentInRange(builder.mM0, -3.15f, 3.15f, "M0");
+ Preconditions.checkArgumentInRange(builder.mDeltaN, -1.18e-8f, 1.18e-8f, "DeltaN");
+ mRootA = builder.mRootA;
+ mEccentricity = builder.mEccentricity;
+ mI0 = builder.mI0;
+ mIDot = builder.mIDot;
+ mOmega = builder.mOmega;
+ mOmega0 = builder.mOmega0;
+ mOmegaDot = builder.mOmegaDot;
+ mM0 = builder.mM0;
+ mDeltaN = builder.mDeltaN;
+ mSecondOrderHarmonicPerturbation = builder.mSecondOrderHarmonicPerturbation;
+ }
+
+ /** Get the square root of the semi-major axis in square root of meters. */
+ @FloatRange(from = 0.0f, to = 8192.0f)
+ public double getRootA() {
+ return mRootA;
+ }
+
+ /** Get the eccentricity. */
+ @FloatRange(from = 0.0f, to = 0.5f)
+ public double getEccentricity() {
+ return mEccentricity;
+ }
+
+ /** Get the inclination angle at reference time in radians. */
+ @FloatRange(from = -3.15f, to = 3.15f)
+ public double getI0() {
+ return mI0;
+ }
+
+ /** Get the rate of change of inclination angle in radians per second. */
+ @FloatRange(from = -2.94e-9f, to = 2.94e-9f)
+ public double getIDot() {
+ return mIDot;
+ }
+
+ /** Get the argument of perigee in radians. */
+ @FloatRange(from = -3.15f, to = 3.15f)
+ public double getOmega() {
+ return mOmega;
+ }
+
+ /** Get the longitude of ascending node of orbit plane at beginning of week in radians. */
+ @FloatRange(from = -3.15f, to = 3.15f)
+ public double getOmega0() {
+ return mOmega0;
+ }
+
+ /** Get the rate of right ascension in radians per second. */
+ @FloatRange(from = -3.1e-6f, to = 3.1e-6f)
+ public double getOmegaDot() {
+ return mOmegaDot;
+ }
+
+ /** Get the mean anomaly at reference time in radians. */
+ @FloatRange(from = -3.15f, to = 3.15f)
+ public double getM0() {
+ return mM0;
+ }
+
+ /** Get the mean motion difference from computed value in radians per second. */
+ @FloatRange(from = -1.18e-8f, to = 1.18e-8f)
+ public double getDeltaN() {
+ return mDeltaN;
+ }
+
+ /** Get the second-order harmonic perturbations. */
+ @NonNull
+ public SecondOrderHarmonicPerturbation getSecondOrderHarmonicPerturbation() {
+ return mSecondOrderHarmonicPerturbation;
+ }
+
+ public static final @NonNull Creator<KeplerianOrbitModel> CREATOR =
+ new Creator<KeplerianOrbitModel>() {
+ @Override
+ @NonNull
+ public KeplerianOrbitModel createFromParcel(Parcel in) {
+ final KeplerianOrbitModel.Builder keplerianOrbitModel =
+ new Builder()
+ .setRootA(in.readDouble())
+ .setEccentricity(in.readDouble())
+ .setI0(in.readDouble())
+ .setIDot(in.readDouble())
+ .setOmega(in.readDouble())
+ .setOmega0(in.readDouble())
+ .setOmegaDot(in.readDouble())
+ .setM0(in.readDouble())
+ .setDeltaN(in.readDouble())
+ .setSecondOrderHarmonicPerturbation(
+ in.readTypedObject(
+ SecondOrderHarmonicPerturbation.CREATOR));
+ return keplerianOrbitModel.build();
+ }
+
+ @Override
+ public KeplerianOrbitModel[] newArray(int size) {
+ return new KeplerianOrbitModel[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeDouble(mRootA);
+ parcel.writeDouble(mEccentricity);
+ parcel.writeDouble(mI0);
+ parcel.writeDouble(mIDot);
+ parcel.writeDouble(mOmega);
+ parcel.writeDouble(mOmega0);
+ parcel.writeDouble(mOmegaDot);
+ parcel.writeDouble(mM0);
+ parcel.writeDouble(mDeltaN);
+ parcel.writeTypedObject(mSecondOrderHarmonicPerturbation, flags);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("KeplerianOrbitModel[");
+ builder.append("rootA = ").append(mRootA);
+ builder.append(", eccentricity = ").append(mEccentricity);
+ builder.append(", i0 = ").append(mI0);
+ builder.append(", iDot = ").append(mIDot);
+ builder.append(", omega = ").append(mOmega);
+ builder.append(", omega0 = ").append(mOmega0);
+ builder.append(", omegaDot = ").append(mOmegaDot);
+ builder.append(", m0 = ").append(mM0);
+ builder.append(", deltaN = ").append(mDeltaN);
+ builder.append(", secondOrderHarmonicPerturbation = ")
+ .append(mSecondOrderHarmonicPerturbation);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link KeplerianOrbitModel} */
+ public static final class Builder {
+ private double mRootA;
+ private double mEccentricity;
+ private double mI0;
+ private double mIDot;
+ private double mOmega;
+ private double mOmega0;
+ private double mOmegaDot;
+ private double mM0;
+ private double mDeltaN;
+ private SecondOrderHarmonicPerturbation mSecondOrderHarmonicPerturbation;
+
+ /** Sets the square root of the semi-major axis in square root of meters. */
+ @NonNull
+ public Builder setRootA(@FloatRange(from = 0.0f, to = 8192.0f) double rootA) {
+ mRootA = rootA;
+ return this;
+ }
+
+ /** Sets the eccentricity. */
+ @NonNull
+ public Builder setEccentricity(@FloatRange(from = 0.0f, to = 0.5f) double eccentricity) {
+ mEccentricity = eccentricity;
+ return this;
+ }
+
+ /** Sets the inclination angle at reference time in radians. */
+ @NonNull
+ public Builder setI0(@FloatRange(from = -3.15f, to = 3.15f) double i0) {
+ mI0 = i0;
+ return this;
+ }
+
+ /** Sets the rate of change of inclination angle in radians per second. */
+ @NonNull
+ public Builder setIDot(@FloatRange(from = -2.94e-9f, to = 2.94e-9f) double iDot) {
+ mIDot = iDot;
+ return this;
+ }
+
+ /** Sets the argument of perigee in radians. */
+ @NonNull
+ public Builder setOmega(@FloatRange(from = -3.15f, to = 3.15f) double omega) {
+ mOmega = omega;
+ return this;
+ }
+
+ /**
+ * Sets the longitude of ascending node of orbit plane at beginning of week in radians.
+ */
+ @NonNull
+ public Builder setOmega0(@FloatRange(from = -3.15f, to = 3.15f) double omega0) {
+ mOmega0 = omega0;
+ return this;
+ }
+
+ /** Sets the rate of right ascension in radians per second. */
+ @NonNull
+ public Builder setOmegaDot(@FloatRange(from = -3.1e-6f, to = 3.1e-6f) double omegaDot) {
+ mOmegaDot = omegaDot;
+ return this;
+ }
+
+ /** Sets the mean anomaly at reference time in radians. */
+ @NonNull
+ public Builder setM0(@FloatRange(from = -3.15f, to = 3.15f) double m0) {
+ mM0 = m0;
+ return this;
+ }
+
+ /** Sets the mean motion difference from computed value in radians per second. */
+ @NonNull
+ public Builder setDeltaN(@FloatRange(from = -1.18e-8f, to = 1.18e-8f) double deltaN) {
+ mDeltaN = deltaN;
+ return this;
+ }
+
+ /** Sets the second-order harmonic perturbations. */
+ @NonNull
+ public Builder setSecondOrderHarmonicPerturbation(
+ @NonNull SecondOrderHarmonicPerturbation secondOrderHarmonicPerturbation) {
+ mSecondOrderHarmonicPerturbation = secondOrderHarmonicPerturbation;
+ return this;
+ }
+
+ /** Builds a {@link KeplerianOrbitModel} instance as specified by this builder. */
+ @NonNull
+ public KeplerianOrbitModel build() {
+ return new KeplerianOrbitModel(this);
+ }
+ }
+
+ /** A class contains second-order harmonic perturbations. */
+ public static final class SecondOrderHarmonicPerturbation implements Parcelable {
+ /** Amplitude of cosine harmonic correction term to angle of inclination in radians. */
+ private final double mCic;
+
+ /** Amplitude of sine harmonic correction term to angle of inclination in radians. */
+ private final double mCis;
+
+ /** Amplitude of cosine harmonic correction term to the orbit in meters. */
+ private final double mCrc;
+
+ /** Amplitude of sine harmonic correction term to the orbit in meters. */
+ private final double mCrs;
+
+ /** Amplitude of cosine harmonic correction term to the argument of latitude in radians. */
+ private final double mCuc;
+
+ /** Amplitude of sine harmonic correction term to the argument of latitude in radians. */
+ private final double mCus;
+
+ private SecondOrderHarmonicPerturbation(Builder builder) {
+ Preconditions.checkArgumentInRange(builder.mCic, -6.11e-5f, 6.11e-5f, "Cic");
+ Preconditions.checkArgumentInRange(builder.mCis, -6.11e-5f, 6.11e-5f, "Cis");
+ Preconditions.checkArgumentInRange(builder.mCrc, -2048.0f, 2048.0f, "Crc");
+ Preconditions.checkArgumentInRange(builder.mCrs, -2048.0f, 2048.0f, "Crs");
+ Preconditions.checkArgumentInRange(builder.mCuc, -6.11e-5f, 6.11e-5f, "Cuc");
+ Preconditions.checkArgumentInRange(builder.mCus, -6.11e-5f, 6.11e-5f, "Cus");
+ mCic = builder.mCic;
+ mCrc = builder.mCrc;
+ mCis = builder.mCis;
+ mCrs = builder.mCrs;
+ mCuc = builder.mCuc;
+ mCus = builder.mCus;
+ }
+
+ /**
+ * Get the amplitude of cosine harmonic correction term to angle of inclination in radians.
+ */
+ @FloatRange(from = -6.11e-5f, to = 6.11e-5f)
+ public double getCic() {
+ return mCic;
+ }
+
+ /**
+ * Get the amplitude of sine harmonic correction term to angle of inclination in radians.
+ */
+ @FloatRange(from = -6.11e-5f, to = 6.11e-5f)
+ public double getCis() {
+ return mCis;
+ }
+
+ /** Get the amplitude of cosine harmonic correction term to the orbit in meters. */
+ @FloatRange(from = -2048.0f, to = 2048.0f)
+ public double getCrc() {
+ return mCrc;
+ }
+
+ /** Get the amplitude of sine harmonic correction term to the orbit in meters. */
+ @FloatRange(from = -2048.0f, to = 2048.0f)
+ public double getCrs() {
+ return mCrs;
+ }
+
+ /**
+ * Get the amplitude of cosine harmonic correction term to the argument of latitude in
+ * radians.
+ */
+ @FloatRange(from = -6.11e-5f, to = 6.11e-5f)
+ public double getCuc() {
+ return mCuc;
+ }
+
+ /**
+ * Get the amplitude of sine harmonic correction term to the argument of latitude in
+ * radians.
+ */
+ @FloatRange(from = -6.11e-5f, to = 6.11e-5f)
+ public double getCus() {
+ return mCus;
+ }
+
+ public static final @NonNull Creator<SecondOrderHarmonicPerturbation> CREATOR =
+ new Creator<SecondOrderHarmonicPerturbation>() {
+ @Override
+ @NonNull
+ public SecondOrderHarmonicPerturbation createFromParcel(Parcel in) {
+ final SecondOrderHarmonicPerturbation.Builder
+ secondOrderHarmonicPerturbation =
+ new Builder()
+ .setCic(in.readDouble())
+ .setCis(in.readDouble())
+ .setCrc(in.readDouble())
+ .setCrs(in.readDouble())
+ .setCuc(in.readDouble())
+ .setCus(in.readDouble());
+ return secondOrderHarmonicPerturbation.build();
+ }
+
+ @Override
+ public SecondOrderHarmonicPerturbation[] newArray(int size) {
+ return new SecondOrderHarmonicPerturbation[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeDouble(mCic);
+ parcel.writeDouble(mCis);
+ parcel.writeDouble(mCrc);
+ parcel.writeDouble(mCrs);
+ parcel.writeDouble(mCuc);
+ parcel.writeDouble(mCus);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("SecondOrderHarmonicPerturbation[");
+ builder.append("cic = ").append(mCic);
+ builder.append(", cis = ").append(mCis);
+ builder.append(", crc = ").append(mCrc);
+ builder.append(", crs = ").append(mCrs);
+ builder.append(", cuc = ").append(mCuc);
+ builder.append(", cus = ").append(mCus);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link SecondOrderHarmonicPerturbation} */
+ public static final class Builder {
+ private double mCic;
+ private double mCis;
+ private double mCrc;
+ private double mCrs;
+ private double mCuc;
+ private double mCus;
+
+ /**
+ * Sets the amplitude of cosine harmonic correction term to angle of inclination in
+ * radians.
+ */
+ @NonNull
+ public Builder setCic(@FloatRange(from = -6.11e-5f, to = 6.11e-5f) double cic) {
+ mCic = cic;
+ return this;
+ }
+
+ /**
+ * Sets the amplitude of sine harmonic correction term to angle of inclination in
+ * radians.
+ */
+ @NonNull
+ public Builder setCis(@FloatRange(from = -6.11e-5f, to = 6.11e-5f) double cis) {
+ mCis = cis;
+ return this;
+ }
+
+ /** Sets the amplitude of cosine harmonic correction term to the orbit in meters. */
+ @NonNull
+ public Builder setCrc(@FloatRange(from = -2048.0f, to = 2048.0f) double crc) {
+ mCrc = crc;
+ return this;
+ }
+
+ /** Sets the amplitude of sine harmonic correction term to the orbit in meters. */
+ @NonNull
+ public Builder setCrs(@FloatRange(from = -2048.0f, to = 2048.0f) double crs) {
+ mCrs = crs;
+ return this;
+ }
+
+ /**
+ * Sets the amplitude of cosine harmonic correction term to the argument of latitude in
+ * radians.
+ */
+ @NonNull
+ public Builder setCuc(@FloatRange(from = -6.11e-5f, to = 6.11e-5f) double cuc) {
+ mCuc = cuc;
+ return this;
+ }
+
+ /**
+ * Sets the amplitude of sine harmonic correction term to the argument of latitude in
+ * radians.
+ */
+ @NonNull
+ public Builder setCus(@FloatRange(from = -6.11e-5f, to = 6.11e-5f) double cus) {
+ mCus = cus;
+ return this;
+ }
+
+ /**
+ * Builds a {@link SecondOrderHarmonicPerturbation} instance as specified by this
+ * builder.
+ */
+ @NonNull
+ public SecondOrderHarmonicPerturbation build() {
+ return new SecondOrderHarmonicPerturbation(this);
+ }
+ }
+ }
+}
diff --git a/location/java/android/location/KlobucharIonosphericModel.java b/location/java/android/location/KlobucharIonosphericModel.java
new file mode 100644
index 0000000..d239c87
--- /dev/null
+++ b/location/java/android/location/KlobucharIonosphericModel.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class contains Klobuchar ionospheric model coefficients used by GPS, BDS, QZSS.
+ *
+ * <p>This is defined in IS-GPS-200 section 20.3.3.5.1.7.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class KlobucharIonosphericModel implements Parcelable {
+ /** Alpha0 coefficientin seconds. */
+ double mAlpha0;
+ /** Alpha1 coefficient in seconds per semi-circle. */
+ double mAlpha1;
+ /** Alpha2 coefficient in seconds per semi-circle squared. */
+ double mAlpha2;
+ /** Alpha3 coefficient in seconds per semi-circle cubed. */
+ double mAlpha3;
+ /** Beta0 coefficient in seconds. */
+ double mBeta0;
+ /** Beta1 coefficient in seconds per semi-circle. */
+ double mBeta1;
+ /** Beta2 coefficient in seconds per semi-circle squared. */
+ double mBeta2;
+ /** Beta3 coefficient in seconds per semi-circle cubed. */
+ double mBeta3;
+
+ private KlobucharIonosphericModel(Builder builder) {
+ Preconditions.checkArgumentInRange(builder.mAlpha0, -1.193e-7f, 1.193e-7f, "Alpha0");
+ Preconditions.checkArgumentInRange(builder.mAlpha1, -9.54e-7f, 9.54e-7f, "Alpha1");
+ Preconditions.checkArgumentInRange(builder.mAlpha2, -7.63e-6f, 7.63e-6f, "Alpha2");
+ Preconditions.checkArgumentInRange(builder.mAlpha3, -7.63e-6f, 7.63e-6f, "Alpha3");
+ Preconditions.checkArgumentInRange(builder.mBeta0, -262144.0f, 262144.0f, "Beta0");
+ Preconditions.checkArgumentInRange(builder.mBeta1, -2097152.0f, 2097152.0f, "Beta1");
+ Preconditions.checkArgumentInRange(builder.mBeta2, -8388608.0f, 8388608.0f, "Beta2");
+ Preconditions.checkArgumentInRange(builder.mBeta3, -8388608.0f, 8388608.0f, "Beta3");
+ mAlpha0 = builder.mAlpha0;
+ mAlpha1 = builder.mAlpha1;
+ mAlpha2 = builder.mAlpha2;
+ mAlpha3 = builder.mAlpha3;
+ mBeta0 = builder.mBeta0;
+ mBeta1 = builder.mBeta1;
+ mBeta2 = builder.mBeta2;
+ mBeta3 = builder.mBeta3;
+ }
+
+ /** Returns the alpha0 coefficient in seconds. */
+ @FloatRange(from = -1.193e-7f, to = 1.193e-7f)
+ public double getAlpha0() {
+ return mAlpha0;
+ }
+
+ /** Returns the alpha1 coefficient in seconds per semi-circle. */
+ @FloatRange(from = -9.54e-7f, to = 9.54e-7f)
+ public double getAlpha1() {
+ return mAlpha1;
+ }
+
+ /** Returns the alpha2 coefficient in seconds per semi-circle squared. */
+ @FloatRange(from = -7.63e-6f, to = 7.63e-6f)
+ public double getAlpha2() {
+ return mAlpha2;
+ }
+
+ /** Returns the alpha3 coefficient in seconds per semi-circle cubed. */
+ @FloatRange(from = -7.63e-6f, to = 7.63e-6f)
+ public double getAlpha3() {
+ return mAlpha3;
+ }
+
+ /** Returns the beta0 coefficient in seconds. */
+ @FloatRange(from = -262144.0f, to = 262144.0f)
+ public double getBeta0() {
+ return mBeta0;
+ }
+
+ /** Returns the beta1 coefficient in seconds per semi-circle. */
+ @FloatRange(from = -2097152.0f, to = 2097152.0f)
+ public double getBeta1() {
+ return mBeta1;
+ }
+
+ /** Returns the beta2 coefficient in seconds per semi-circle squared. */
+ @FloatRange(from = -8388608.0f, to = 8388608.0f)
+ public double getBeta2() {
+ return mBeta2;
+ }
+
+ /** Returns the beta3 coefficient in seconds per semi-circle cubed. */
+ @FloatRange(from = -8388608.0f, to = 8388608.0f)
+ public double getBeta3() {
+ return mBeta3;
+ }
+
+ public static final @NonNull Creator<KlobucharIonosphericModel> CREATOR =
+ new Creator<KlobucharIonosphericModel>() {
+ @Override
+ @NonNull
+ public KlobucharIonosphericModel createFromParcel(Parcel in) {
+ return new KlobucharIonosphericModel.Builder()
+ .setAlpha0(in.readDouble())
+ .setAlpha1(in.readDouble())
+ .setAlpha2(in.readDouble())
+ .setAlpha3(in.readDouble())
+ .setBeta0(in.readDouble())
+ .setBeta1(in.readDouble())
+ .setBeta2(in.readDouble())
+ .setBeta3(in.readDouble())
+ .build();
+ }
+ @Override
+ public KlobucharIonosphericModel[] newArray(int size) {
+ return new KlobucharIonosphericModel[size];
+ }
+ };
+
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeDouble(mAlpha0);
+ parcel.writeDouble(mAlpha1);
+ parcel.writeDouble(mAlpha2);
+ parcel.writeDouble(mAlpha3);
+ parcel.writeDouble(mBeta0);
+ parcel.writeDouble(mBeta1);
+ parcel.writeDouble(mBeta2);
+ parcel.writeDouble(mBeta3);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("KlobucharIonosphericModel[");
+ builder.append("alpha0 = ").append(mAlpha0);
+ builder.append(", alpha1 = ").append(mAlpha1);
+ builder.append(", alpha2 = ").append(mAlpha2);
+ builder.append(", alpha3 = ").append(mAlpha3);
+ builder.append(", beta0 = ").append(mBeta0);
+ builder.append(", beta1 = ").append(mBeta1);
+ builder.append(", beta2 = ").append(mBeta2);
+ builder.append(", beta3 = ").append(mBeta3);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link KlobucharIonosphericModel} */
+ public static final class Builder {
+ private double mAlpha0;
+ private double mAlpha1;
+ private double mAlpha2;
+ private double mAlpha3;
+ private double mBeta0;
+ private double mBeta1;
+ private double mBeta2;
+ private double mBeta3;
+
+ /** Sets the alpha0 coefficient in seconds. */
+ @NonNull
+ public Builder setAlpha0(@FloatRange(from = -1.193e-7f, to = 1.193e-7f) double alpha0) {
+ mAlpha0 = alpha0;
+ return this;
+ }
+
+ /** Sets the alpha1 coefficient in seconds per semi-circle. */
+ @NonNull
+ public Builder setAlpha1(@FloatRange(from = -9.54e-7f, to = 9.54e-7f) double alpha1) {
+ mAlpha1 = alpha1;
+ return this;
+ }
+
+ /** Sets the alpha2 coefficient in seconds per semi-circle squared. */
+ @NonNull
+ public Builder setAlpha2(@FloatRange(from = -7.63e-6f, to = 7.63e-6f) double alpha2) {
+ mAlpha2 = alpha2;
+ return this;
+ }
+
+ /** Sets the alpha3 coefficient in seconds per semi-circle cubed. */
+ @NonNull
+ public Builder setAlpha3(@FloatRange(from = -7.63e-6f, to = 7.63e-6f) double alpha3) {
+ mAlpha3 = alpha3;
+ return this;
+ }
+
+ /** Sets the beta0 coefficient in seconds. */
+ @NonNull
+ public Builder setBeta0(@FloatRange(from = -262144.0f, to = 262144.0f) double beta0) {
+ mBeta0 = beta0;
+ return this;
+ }
+
+ /** Sets the beta1 coefficient in seconds per semi-circle. */
+ @NonNull
+ public Builder setBeta1(@FloatRange(from = -2097152.0f, to = 2097152.0f) double beta1) {
+ mBeta1 = beta1;
+ return this;
+ }
+
+ /** Sets the beta2 coefficient in seconds per semi-circle squared. */
+ @NonNull
+ public Builder setBeta2(@FloatRange(from = -8388608.0f, to = 8388608.0f) double beta2) {
+ mBeta2 = beta2;
+ return this;
+ }
+
+ /** Sets the beta3 coefficient in seconds per semi-circle cubed. */
+ @NonNull
+ public Builder setBeta3(@FloatRange(from = -8388608.0f, to = 8388608.0f) double beta3) {
+ mBeta3 = beta3;
+ return this;
+ }
+
+ /** Builds a {@link KlobucharIonosphericModel} instance as specified by this builder. */
+ @NonNull
+ public KlobucharIonosphericModel build() {
+ return new KlobucharIonosphericModel(this);
+ }
+ }
+}
diff --git a/location/java/android/location/LeapSecondsModel.java b/location/java/android/location/LeapSecondsModel.java
new file mode 100644
index 0000000..bc132ad
--- /dev/null
+++ b/location/java/android/location/LeapSecondsModel.java
@@ -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 android.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Contains the leap seconds set of parameters needed for GNSS time.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class LeapSecondsModel implements Parcelable {
+ /** Time difference due to leap seconds before the event in seconds. (UTC) */
+ private final int mLeapSeconds;
+
+ /** Time difference due to leap seconds after the event in seconds. (UTC) */
+ private final int mLeapSecondsFuture;
+
+ /** GNSS week number in which the leap second event will occur. (UTC) */
+ private final int mWeekNumberLeapSecondsFuture;
+
+ /** Day number when the next leap second will occur. */
+ private final int mDayNumberLeapSecondsFuture;
+
+ private LeapSecondsModel(Builder builder) {
+ Preconditions.checkArgument(builder.mLeapSeconds >= 0);
+ Preconditions.checkArgument(builder.mLeapSecondsFuture >= 0);
+ Preconditions.checkArgument(builder.mWeekNumberLeapSecondsFuture >= 0);
+ Preconditions.checkArgument(builder.mDayNumberLeapSecondsFuture >= 0);
+ mLeapSeconds = builder.mLeapSeconds;
+ mLeapSecondsFuture = builder.mLeapSecondsFuture;
+ mWeekNumberLeapSecondsFuture = builder.mWeekNumberLeapSecondsFuture;
+ mDayNumberLeapSecondsFuture = builder.mDayNumberLeapSecondsFuture;
+ }
+
+ /** Returns the time difference due to leap seconds before the event in seconds. (UTC) */
+ @IntRange(from = 0)
+ public int getLeapSeconds() {
+ return mLeapSeconds;
+ }
+
+ /** Returns the time difference due to leap seconds after the event in seconds. (UTC) */
+ @IntRange(from = 0)
+ public int getLeapSecondsFuture() {
+ return mLeapSecondsFuture;
+ }
+
+ /** Returns the GNSS week number in which the leap second event will occur. (UTC) */
+ @IntRange(from = 0)
+ public int getWeekNumberLeapSecondsFuture() {
+ return mWeekNumberLeapSecondsFuture;
+ }
+
+ /** Returns the day number when the next leap second will occur. */
+ @IntRange(from = 0)
+ public int getDayNumberLeapSecondsFuture() {
+ return mDayNumberLeapSecondsFuture;
+ }
+
+ public static final @NonNull Creator<LeapSecondsModel> CREATOR =
+ new Creator<LeapSecondsModel>() {
+ @Override
+ @NonNull
+ public LeapSecondsModel createFromParcel(Parcel in) {
+ final LeapSecondsModel.Builder leapSecondsModel = new Builder();
+ leapSecondsModel.setLeapSeconds(in.readInt());
+ leapSecondsModel.setLeapSecondsFuture(in.readInt());
+ leapSecondsModel.setWeekNumberLeapSecondsFuture(in.readInt());
+ leapSecondsModel.setDayNumberLeapSecondsFuture(in.readInt());
+ return leapSecondsModel.build();
+ }
+
+ @Override
+ public LeapSecondsModel[] newArray(int size) {
+ return new LeapSecondsModel[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mLeapSeconds);
+ parcel.writeInt(mLeapSecondsFuture);
+ parcel.writeInt(mWeekNumberLeapSecondsFuture);
+ parcel.writeInt(mDayNumberLeapSecondsFuture);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("LeapSecondsModel[");
+ builder.append("leapSeconds = ").append(mLeapSeconds);
+ builder.append(", leapSecondsFuture = ").append(mLeapSecondsFuture);
+ builder.append(", weekNumberLeapSecondsFuture = ").append(mWeekNumberLeapSecondsFuture);
+ builder.append(", dayNumberLeapSecondsFuture = ").append(mDayNumberLeapSecondsFuture);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link LeapSecondsModel} */
+ public static final class Builder {
+ private int mLeapSeconds;
+ private int mLeapSecondsFuture;
+ private int mWeekNumberLeapSecondsFuture;
+ private int mDayNumberLeapSecondsFuture;
+
+ /** Sets the time difference due to leap seconds before the event in seconds. (UTC) */
+ @NonNull
+ public Builder setLeapSeconds(@IntRange(from = 0) int leapSeconds) {
+ mLeapSeconds = leapSeconds;
+ return this;
+ }
+
+ /** Sets the time difference due to leap seconds after the event in seconds. (UTC) */
+ @NonNull
+ public Builder setLeapSecondsFuture(@IntRange(from = 0) int leapSecondsFuture) {
+ mLeapSecondsFuture = leapSecondsFuture;
+ return this;
+ }
+
+ /** Sets the GNSS week number in which the leap second event will occur. (UTC) */
+ @NonNull
+ public Builder setWeekNumberLeapSecondsFuture(
+ @IntRange(from = 0) int weekNumberLeapSecondsFuture) {
+ mWeekNumberLeapSecondsFuture = weekNumberLeapSecondsFuture;
+ return this;
+ }
+
+ /** Sets the day number when the next leap second will occur. */
+ @NonNull
+ public Builder setDayNumberLeapSecondsFuture(
+ @IntRange(from = 0) int dayNumberLeapSecondsFuture) {
+ mDayNumberLeapSecondsFuture = dayNumberLeapSecondsFuture;
+ return this;
+ }
+
+ /** Builds a {@link LeapSecondsModel} instance as specified by this builder. */
+ @NonNull
+ public LeapSecondsModel build() {
+ return new LeapSecondsModel(this);
+ }
+ }
+}
diff --git a/location/java/android/location/QzssAssistance.java b/location/java/android/location/QzssAssistance.java
new file mode 100644
index 0000000..9383ce3
--- /dev/null
+++ b/location/java/android/location/QzssAssistance.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.location.GnssAssistance.GnssSatelliteCorrections;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class contains QZSS assistance.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class QzssAssistance implements Parcelable {
+
+ /** The QZSS almanac. */
+ @Nullable private final GnssAlmanac mAlmanac;
+
+ /** The Klobuchar ionospheric model. */
+ @Nullable private final KlobucharIonosphericModel mIonosphericModel;
+
+ /** The UTC model. */
+ @Nullable private final UtcModel mUtcModel;
+
+ /** The leap seconds model. */
+ @Nullable private final LeapSecondsModel mLeapSecondsModel;
+
+ /** The list of time models. */
+ @NonNull private final List<TimeModel> mTimeModels;
+
+ /** The list of QZSS ephemeris. */
+ @NonNull private final List<QzssSatelliteEphemeris> mSatelliteEphemeris;
+
+ /** The list of real time integrity models. */
+ @NonNull private final List<RealTimeIntegrityModel> mRealTimeIntegrityModels;
+
+ /** The list of QZSS satellite corrections. */
+ @NonNull private final List<GnssSatelliteCorrections> mSatelliteCorrections;
+
+ private QzssAssistance(Builder builder) {
+ mAlmanac = builder.mAlmanac;
+ mIonosphericModel = builder.mIonosphericModel;
+ mUtcModel = builder.mUtcModel;
+ mLeapSecondsModel = builder.mLeapSecondsModel;
+ if (builder.mTimeModels != null) {
+ mTimeModels = Collections.unmodifiableList(new ArrayList<>(builder.mTimeModels));
+ } else {
+ mTimeModels = new ArrayList<>();
+ }
+ if (builder.mSatelliteEphemeris != null) {
+ mSatelliteEphemeris =
+ Collections.unmodifiableList(new ArrayList<>(builder.mSatelliteEphemeris));
+ } else {
+ mSatelliteEphemeris = new ArrayList<>();
+ }
+ if (builder.mRealTimeIntegrityModels != null) {
+ mRealTimeIntegrityModels =
+ Collections.unmodifiableList(new ArrayList<>(builder.mRealTimeIntegrityModels));
+ } else {
+ mRealTimeIntegrityModels = new ArrayList<>();
+ }
+ if (builder.mSatelliteCorrections != null) {
+ mSatelliteCorrections =
+ Collections.unmodifiableList(new ArrayList<>(builder.mSatelliteCorrections));
+ } else {
+ mSatelliteCorrections = new ArrayList<>();
+ }
+ }
+
+ /** Returns the QZSS almanac. */
+ @Nullable
+ public GnssAlmanac getAlmanac() {
+ return mAlmanac;
+ }
+
+ /** Returns the Klobuchar ionospheric model. */
+ @Nullable
+ public KlobucharIonosphericModel getIonosphericModel() {
+ return mIonosphericModel;
+ }
+
+ /** Returns the UTC model. */
+ @Nullable
+ public UtcModel getUtcModel() {
+ return mUtcModel;
+ }
+
+ /** Returns the leap seconds model. */
+ @Nullable
+ public LeapSecondsModel getLeapSecondsModel() {
+ return mLeapSecondsModel;
+ }
+
+ /** Returns the list of time models. */
+ @NonNull
+ public List<TimeModel> getTimeModels() {
+ return mTimeModels;
+ }
+
+ /** Returns the list of QZSS ephemeris. */
+ @NonNull
+ public List<QzssSatelliteEphemeris> getSatelliteEphemeris() {
+ return mSatelliteEphemeris;
+ }
+
+ /** Returns the list of real time integrity models. */
+ @NonNull
+ public List<RealTimeIntegrityModel> getRealTimeIntegrityModels() {
+ return mRealTimeIntegrityModels;
+ }
+
+ /** Returns the list of QZSS satellite corrections. */
+ @NonNull
+ public List<GnssSatelliteCorrections> getSatelliteCorrections() {
+ return mSatelliteCorrections;
+ }
+
+ public static final @NonNull Creator<QzssAssistance> CREATOR =
+ new Creator<QzssAssistance>() {
+ @Override
+ @NonNull
+ public QzssAssistance createFromParcel(Parcel in) {
+ return new QzssAssistance.Builder()
+ .setAlmanac(in.readTypedObject(GnssAlmanac.CREATOR))
+ .setIonosphericModel(in.readTypedObject(KlobucharIonosphericModel.CREATOR))
+ .setUtcModel(in.readTypedObject(UtcModel.CREATOR))
+ .setLeapSecondsModel(in.readTypedObject(LeapSecondsModel.CREATOR))
+ .setTimeModels(in.createTypedArrayList(TimeModel.CREATOR))
+ .setSatelliteEphemeris(
+ in.createTypedArrayList(QzssSatelliteEphemeris.CREATOR))
+ .setRealTimeIntegrityModels(
+ in.createTypedArrayList(RealTimeIntegrityModel.CREATOR))
+ .setSatelliteCorrections(
+ in.createTypedArrayList(GnssSatelliteCorrections.CREATOR))
+ .build();
+ }
+ @Override
+ public QzssAssistance[] newArray(int size) {
+ return new QzssAssistance[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(mAlmanac, flags);
+ dest.writeTypedObject(mIonosphericModel, flags);
+ dest.writeTypedObject(mUtcModel, flags);
+ dest.writeTypedObject(mLeapSecondsModel, flags);
+ dest.writeTypedList(mTimeModels);
+ dest.writeTypedList(mSatelliteEphemeris);
+ dest.writeTypedList(mRealTimeIntegrityModels);
+ dest.writeTypedList(mSatelliteCorrections);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("QzssAssistance[");
+ builder.append("almanac = ").append(mAlmanac);
+ builder.append(", ionosphericModel = ").append(mIonosphericModel);
+ builder.append(", utcModel = ").append(mUtcModel);
+ builder.append(", leapSecondsModel = ").append(mLeapSecondsModel);
+ builder.append(", timeModels = ").append(mTimeModels);
+ builder.append(", satelliteEphemeris = ").append(mSatelliteEphemeris);
+ builder.append(", realTimeIntegrityModels = ").append(mRealTimeIntegrityModels);
+ builder.append(", satelliteCorrections = ").append(mSatelliteCorrections);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link QzssAssistance}. */
+ public static final class Builder {
+ private GnssAlmanac mAlmanac;
+ private KlobucharIonosphericModel mIonosphericModel;
+ private UtcModel mUtcModel;
+ private LeapSecondsModel mLeapSecondsModel;
+ private List<TimeModel> mTimeModels;
+ private List<QzssSatelliteEphemeris> mSatelliteEphemeris;
+ private List<RealTimeIntegrityModel> mRealTimeIntegrityModels;
+ private List<GnssSatelliteCorrections> mSatelliteCorrections;
+
+ /** Sets the QZSS almanac. */
+ @NonNull
+ public Builder setAlmanac(@Nullable GnssAlmanac almanac) {
+ mAlmanac = almanac;
+ return this;
+ }
+
+ /** Sets the Klobuchar ionospheric model. */
+ @NonNull
+ public Builder setIonosphericModel(@Nullable KlobucharIonosphericModel ionosphericModel) {
+ mIonosphericModel = ionosphericModel;
+ return this;
+ }
+
+ /** Sets the UTC model. */
+ @NonNull
+ public Builder setUtcModel(@Nullable UtcModel utcModel) {
+ mUtcModel = utcModel;
+ return this;
+ }
+
+ /** Sets the leap seconds model. */
+ @NonNull
+ public Builder setLeapSecondsModel(@Nullable LeapSecondsModel leapSecondsModel) {
+ mLeapSecondsModel = leapSecondsModel;
+ return this;
+ }
+
+ /** Sets the list of time models. */
+ @NonNull
+ public Builder setTimeModels(
+ @Nullable @SuppressLint("NullableCollection") List<TimeModel> timeModels) {
+ mTimeModels = timeModels;
+ return this;
+ }
+
+ /** Sets the list of QZSS ephemeris. */
+ @NonNull
+ public Builder setSatelliteEphemeris(
+ @Nullable @SuppressLint("NullableCollection")
+ List<QzssSatelliteEphemeris> satelliteEphemeris) {
+ mSatelliteEphemeris = satelliteEphemeris;
+ return this;
+ }
+
+ /** Sets the list of real time integrity model. */
+ @NonNull
+ public Builder setRealTimeIntegrityModels(
+ @Nullable @SuppressLint("NullableCollection")
+ List<RealTimeIntegrityModel> realTimeIntegrityModels) {
+ mRealTimeIntegrityModels = realTimeIntegrityModels;
+ return this;
+ }
+
+ /** Sets the list of QZSS satellite correction. */
+ @NonNull
+ public Builder setSatelliteCorrections(
+ @Nullable @SuppressLint("NullableCollection")
+ List<GnssSatelliteCorrections> satelliteCorrections) {
+ mSatelliteCorrections = satelliteCorrections;
+ return this;
+ }
+
+ /** Builds a {@link QzssAssistance} instance as specified by this builder. */
+ @NonNull
+ public QzssAssistance build() {
+ return new QzssAssistance(this);
+ }
+ }
+}
diff --git a/location/java/android/location/QzssSatelliteEphemeris.java b/location/java/android/location/QzssSatelliteEphemeris.java
new file mode 100644
index 0000000..96203d9
--- /dev/null
+++ b/location/java/android/location/QzssSatelliteEphemeris.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.GpsSatelliteEphemeris.GpsL2Params;
+import android.location.GpsSatelliteEphemeris.GpsSatelliteClockModel;
+import android.location.GpsSatelliteEphemeris.GpsSatelliteHealth;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class contains ephemeris parameters specific to QZSS satellites.
+ *
+ * <p>This is defined in IS-QZSS-PNT section 4.1.2.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class QzssSatelliteEphemeris implements Parcelable {
+ /** Satellite PRN. */
+ private final int mPrn;
+
+ /** L2 parameters. */
+ @NonNull private final GpsL2Params mGpsL2Params;
+
+ /** Clock model. */
+ @NonNull private final GpsSatelliteClockModel mSatelliteClockModel;
+
+ /** Orbit model. */
+ @NonNull private final KeplerianOrbitModel mSatelliteOrbitModel;
+
+ /** Satellite health. */
+ @NonNull private final GpsSatelliteHealth mSatelliteHealth;
+
+ /** Ephemeris time. */
+ @NonNull private final SatelliteEphemerisTime mSatelliteEphemerisTime;
+
+ /** Returns the PRN of the satellite. */
+ @IntRange(from = 183, to = 206)
+ public int getPrn() {
+ return mPrn;
+ }
+
+ /** Returns the L2 parameters of the satellite. */
+ @NonNull
+ public GpsL2Params getGpsL2Params() {
+ return mGpsL2Params;
+ }
+
+ /** Returns the clock model of the satellite. */
+ @NonNull
+ public GpsSatelliteClockModel getSatelliteClockModel() {
+ return mSatelliteClockModel;
+ }
+
+ /** Returns the orbit model of the satellite. */
+ @NonNull
+ public KeplerianOrbitModel getSatelliteOrbitModel() {
+ return mSatelliteOrbitModel;
+ }
+
+ /** Returns the satellite health. */
+ @NonNull
+ public GpsSatelliteHealth getSatelliteHealth() {
+ return mSatelliteHealth;
+ }
+
+ /** Returns the ephemeris time. */
+ @NonNull
+ public SatelliteEphemerisTime getSatelliteEphemerisTime() {
+ return mSatelliteEphemerisTime;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mPrn);
+ parcel.writeTypedObject(mGpsL2Params, flags);
+ parcel.writeTypedObject(mSatelliteClockModel, flags);
+ parcel.writeTypedObject(mSatelliteOrbitModel, flags);
+ parcel.writeTypedObject(mSatelliteHealth, flags);
+ parcel.writeTypedObject(mSatelliteEphemerisTime, flags);
+ }
+
+ private QzssSatelliteEphemeris(Builder builder) {
+ // Allow PRN beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mPrn >= 1);
+ Preconditions.checkNotNull(builder.mGpsL2Params, "GpsL2Params cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteClockModel,
+ "SatelliteClockModel cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteOrbitModel,
+ "SatelliteOrbitModel cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteHealth,
+ "SatelliteHealth cannot be null");
+ Preconditions.checkNotNull(builder.mSatelliteEphemerisTime,
+ "SatelliteEphemerisTime cannot be null");
+ mPrn = builder.mPrn;
+ mGpsL2Params = builder.mGpsL2Params;
+ mSatelliteClockModel = builder.mSatelliteClockModel;
+ mSatelliteOrbitModel = builder.mSatelliteOrbitModel;
+ mSatelliteHealth = builder.mSatelliteHealth;
+ mSatelliteEphemerisTime = builder.mSatelliteEphemerisTime;
+ }
+
+ public static final @NonNull Creator<QzssSatelliteEphemeris> CREATOR =
+ new Creator<QzssSatelliteEphemeris>() {
+ @Override
+ @NonNull
+ public QzssSatelliteEphemeris createFromParcel(Parcel in) {
+ final QzssSatelliteEphemeris.Builder qzssSatelliteEphemeris =
+ new Builder()
+ .setPrn(in.readInt())
+ .setGpsL2Params(in.readTypedObject(GpsL2Params.CREATOR))
+ .setSatelliteClockModel(
+ in.readTypedObject(GpsSatelliteClockModel.CREATOR))
+ .setSatelliteOrbitModel(
+ in.readTypedObject(KeplerianOrbitModel.CREATOR))
+ .setSatelliteHealth(
+ in.readTypedObject(GpsSatelliteHealth.CREATOR))
+ .setSatelliteEphemerisTime(
+ in.readTypedObject(SatelliteEphemerisTime.CREATOR));
+ return qzssSatelliteEphemeris.build();
+ }
+
+ @Override
+ public QzssSatelliteEphemeris[] newArray(int size) {
+ return new QzssSatelliteEphemeris[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("QzssSatelliteEphemeris[");
+ builder.append("prn=").append(mPrn);
+ builder.append(", gpsL2Params=").append(mGpsL2Params);
+ builder.append(", satelliteClockModel=").append(mSatelliteClockModel);
+ builder.append(", satelliteOrbitModel=").append(mSatelliteOrbitModel);
+ builder.append(", satelliteHealth=").append(mSatelliteHealth);
+ builder.append(", satelliteEphemerisTime=").append(mSatelliteEphemerisTime);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link QzssSatelliteEphemeris}. */
+ public static final class Builder {
+ private int mPrn;
+ private GpsL2Params mGpsL2Params;
+ private GpsSatelliteClockModel mSatelliteClockModel;
+ private KeplerianOrbitModel mSatelliteOrbitModel;
+ private GpsSatelliteHealth mSatelliteHealth;
+ private SatelliteEphemerisTime mSatelliteEphemerisTime;
+
+ /** Sets the PRN of the satellite. */
+ @NonNull
+ public Builder setPrn(@IntRange(from = 183, to = 206) int prn) {
+ mPrn = prn;
+ return this;
+ }
+
+ /** Sets the L2 parameters of the satellite. */
+ @NonNull
+ public Builder setGpsL2Params(@NonNull GpsL2Params gpsL2Params) {
+ mGpsL2Params = gpsL2Params;
+ return this;
+ }
+
+ /** Sets the clock model of the satellite. */
+ @NonNull
+ public Builder setSatelliteClockModel(@NonNull GpsSatelliteClockModel satelliteClockModel) {
+ mSatelliteClockModel = satelliteClockModel;
+ return this;
+ }
+
+ /** Sets the orbit model of the satellite. */
+ @NonNull
+ public Builder setSatelliteOrbitModel(@NonNull KeplerianOrbitModel satelliteOrbitModel) {
+ mSatelliteOrbitModel = satelliteOrbitModel;
+ return this;
+ }
+
+ /** Sets the satellite health. */
+ @NonNull
+ public Builder setSatelliteHealth(@NonNull GpsSatelliteHealth satelliteHealth) {
+ mSatelliteHealth = satelliteHealth;
+ return this;
+ }
+
+ /** Sets the ephemeris time. */
+ @NonNull
+ public Builder setSatelliteEphemerisTime(
+ @NonNull SatelliteEphemerisTime satelliteEphemerisTime) {
+ mSatelliteEphemerisTime = satelliteEphemerisTime;
+ return this;
+ }
+
+ /** Builds a {@link QzssSatelliteEphemeris} instance as specified by this builder. */
+ @NonNull
+ public QzssSatelliteEphemeris build() {
+ return new QzssSatelliteEphemeris(this);
+ }
+ }
+}
diff --git a/location/java/android/location/RealTimeIntegrityModel.java b/location/java/android/location/RealTimeIntegrityModel.java
new file mode 100644
index 0000000..d268926
--- /dev/null
+++ b/location/java/android/location/RealTimeIntegrityModel.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class contains the real time integrity status of a GNSS satellite based on notice advisory.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class RealTimeIntegrityModel implements Parcelable {
+ /**
+ * Pseudo-random or satellite ID number for the satellite,
+ * a.k.a. Space Vehicle (SV), or OSN number for Glonass.
+ *
+ * <p>The distinction is made by looking at the constellation field. Values
+ * must be in the range of:
+ *
+ * <p> - GPS: 1-32
+ * <p> - GLONASS: 1-25
+ * <p> - QZSS: 183-206
+ * <p> - Galileo: 1-36
+ * <p> - Beidou: 1-63
+ */
+ private final int mSvid;
+
+ /** Indicates whether the satellite is currently usable for navigation. */
+ private final boolean mUsable;
+
+ /** UTC timestamp (in seconds) when the advisory was published. */
+ private final long mPublishDateSeconds;
+
+ /** UTC timestamp (in seconds) for the start of the event. */
+ private final long mStartDateSeconds;
+
+ /** UTC timestamp (in seconds) for the end of the event. */
+ private final long mEndDateSeconds;
+
+ /**
+ * Abbreviated type of the advisory, providing a concise summary of the event.
+ *
+ * <p>This field follows different definitions depending on the GNSS constellation:
+ * <p> - GPS: See NANU type definitions(https://www.navcen.uscg.gov/nanu-abbreviations-and-descriptions)
+ * <p> - Galileo: See NAGU type definitions(https://www.gsc-europa.eu/system-service-status/nagu-information)
+ * <p> - QZSS: See NAQU type definitions](https://sys.qzss.go.jp/dod/en/naqu/type.html)
+ * <p> - BeiDou: Not used; set to an empty string.
+ */
+ @NonNull private final String mAdvisoryType;
+
+ /**
+ * Unique identifier for the advisory within its constellation's system.
+ *
+ * <p>For BeiDou, this is not used and should be an empty string.
+ */
+ @NonNull private final String mAdvisoryNumber;
+
+ private RealTimeIntegrityModel(Builder builder) {
+ // Allow SV ID beyond the range to support potential future extensibility.
+ Preconditions.checkArgument(builder.mSvid >= 1);
+ Preconditions.checkArgument(builder.mPublishDateSeconds > 0);
+ Preconditions.checkArgument(builder.mStartDateSeconds > 0);
+ Preconditions.checkArgument(builder.mEndDateSeconds > 0);
+ Preconditions.checkNotNull(builder.mAdvisoryType, "AdvisoryType cannot be null");
+ Preconditions.checkNotNull(builder.mAdvisoryNumber, "AdvisoryNumber cannot be null");
+ mSvid = builder.mSvid;
+ mUsable = builder.mUsable;
+ mPublishDateSeconds = builder.mPublishDateSeconds;
+ mStartDateSeconds = builder.mStartDateSeconds;
+ mEndDateSeconds = builder.mEndDateSeconds;
+ mAdvisoryType = builder.mAdvisoryType;
+ mAdvisoryNumber = builder.mAdvisoryNumber;
+ }
+
+ /**
+ * Returns the Pseudo-random or satellite ID number for the satellite,
+ * a.k.a. Space Vehicle (SV), or OSN number for Glonass.
+ *
+ * <p>The distinction is made by looking at the constellation field. Values
+ * must be in the range of:
+ *
+ * <p> - GPS: 1-32
+ * <p> - GLONASS: 1-25
+ * <p> - QZSS: 183-206
+ * <p> - Galileo: 1-36
+ * <p> - Beidou: 1-63
+ */
+ @IntRange(from = 1, to = 206)
+ public int getSvid() {
+ return mSvid;
+ }
+
+ /** Returns whether the satellite is usable or not. */
+ public boolean isUsable() {
+ return mUsable;
+ }
+
+ /** Returns the UTC timestamp (in seconds) when the advisory was published */
+ @IntRange(from = 0)
+ public long getPublishDateSeconds() {
+ return mPublishDateSeconds;
+ }
+
+ /** Returns UTC timestamp (in seconds) for the start of the event. */
+ @IntRange(from = 0)
+ public long getStartDateSeconds() {
+ return mStartDateSeconds;
+ }
+
+ /** Returns UTC timestamp (in seconds) for the end of the event. */
+ @IntRange(from = 0)
+ public long getEndDateSeconds() {
+ return mEndDateSeconds;
+ }
+
+ /** Returns the abbreviated type of notice advisory. */
+ @NonNull
+ public String getAdvisoryType() {
+ return mAdvisoryType;
+ }
+
+ /** Returns the unique identifier for the advisory. */
+ @NonNull
+ public String getAdvisoryNumber() {
+ return mAdvisoryNumber;
+ }
+
+ public static final @NonNull Creator<RealTimeIntegrityModel> CREATOR =
+ new Creator<RealTimeIntegrityModel>() {
+ @Override
+ @NonNull
+ public RealTimeIntegrityModel createFromParcel(Parcel in) {
+ RealTimeIntegrityModel realTimeIntegrityModel =
+ new RealTimeIntegrityModel.Builder()
+ .setSvid(in.readInt())
+ .setUsable(in.readBoolean())
+ .setPublishDateSeconds(in.readLong())
+ .setStartDateSeconds(in.readLong())
+ .setEndDateSeconds(in.readLong())
+ .setAdvisoryType(in.readString8())
+ .setAdvisoryNumber(in.readString8())
+ .build();
+ return realTimeIntegrityModel;
+ }
+
+ @Override
+ public RealTimeIntegrityModel[] newArray(int size) {
+ return new RealTimeIntegrityModel[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mSvid);
+ parcel.writeBoolean(mUsable);
+ parcel.writeLong(mPublishDateSeconds);
+ parcel.writeLong(mStartDateSeconds);
+ parcel.writeLong(mEndDateSeconds);
+ parcel.writeString8(mAdvisoryType);
+ parcel.writeString8(mAdvisoryNumber);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("RealTimeIntegrityModel[");
+ builder.append("svid = ").append(mSvid);
+ builder.append(", usable = ").append(mUsable);
+ builder.append(", publishDateSeconds = ").append(mPublishDateSeconds);
+ builder.append(", startDateSeconds = ").append(mStartDateSeconds);
+ builder.append(", endDateSeconds = ").append(mEndDateSeconds);
+ builder.append(", advisoryType = ").append(mAdvisoryType);
+ builder.append(", advisoryNumber = ").append(mAdvisoryNumber);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link RealTimeIntegrityModel} */
+ public static final class Builder {
+ private int mSvid;
+ private boolean mUsable;
+ private long mPublishDateSeconds;
+ private long mStartDateSeconds;
+ private long mEndDateSeconds;
+ private String mAdvisoryType;
+ private String mAdvisoryNumber;
+
+ /**
+ * Sets the Pseudo-random or satellite ID number for the satellite,
+ * a.k.a. Space Vehicle (SV), or OSN number for Glonass.
+ *
+ * <p>The distinction is made by looking at the constellation field. Values
+ * must be in the range of:
+ *
+ * <p> - GPS: 1-32
+ * <p> - GLONASS: 1-25
+ * <p> - QZSS: 183-206
+ * <p> - Galileo: 1-36
+ * <p> - Beidou: 1-63
+ */
+ @NonNull
+ public Builder setSvid(@IntRange(from = 1, to = 206) int svid) {
+ mSvid = svid;
+ return this;
+ }
+
+ /** Sets whether the satellite is usable or not. */
+ @NonNull
+ public Builder setUsable(boolean usable) {
+ mUsable = usable;
+ return this;
+ }
+
+ /** Sets the UTC timestamp (in seconds) when the advisory was published. */
+ @NonNull
+ public Builder setPublishDateSeconds(@IntRange(from = 0) long publishDateSeconds) {
+ mPublishDateSeconds = publishDateSeconds;
+ return this;
+ }
+
+ /** Sets the UTC timestamp (in seconds) for the start of the event. */
+ @NonNull
+ public Builder setStartDateSeconds(@IntRange(from = 0) long startDateSeconds) {
+ mStartDateSeconds = startDateSeconds;
+ return this;
+ }
+
+ /** Sets the UTC timestamp (in seconds) for the end of the event. */
+ @NonNull
+ public Builder setEndDateSeconds(@IntRange(from = 0) long endDateSeconds) {
+ mEndDateSeconds = endDateSeconds;
+ return this;
+ }
+
+ /** Sets the abbreviated type of notice advisory. */
+ @NonNull
+ public Builder setAdvisoryType(@NonNull String advisoryType) {
+ mAdvisoryType = advisoryType;
+ return this;
+ }
+
+ /** Sets the unique identifier for the advisory. */
+ @NonNull
+ public Builder setAdvisoryNumber(@NonNull String advisoryNumber) {
+ mAdvisoryNumber = advisoryNumber;
+ return this;
+ }
+
+ /** Builds a {@link RealTimeIntegrityModel} instance as specified by this builder. */
+ @NonNull
+ public RealTimeIntegrityModel build() {
+ return new RealTimeIntegrityModel(this);
+ }
+ }
+}
diff --git a/location/java/android/location/SatelliteEphemerisTime.java b/location/java/android/location/SatelliteEphemerisTime.java
new file mode 100644
index 0000000..0ab3acf
--- /dev/null
+++ b/location/java/android/location/SatelliteEphemerisTime.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class contains time of ephemeris for GPS, Galileo, and QZSS.
+ *
+ * <p>For GPS, this is defined in IS-GPS-200, section 20.3.3.4.1.
+ * <p>For Galileo, this is defined in Galileo-OS-SIS-ICD, section 5.1.2, 5.1.9.2.
+ * <p>For QZSS, this is defined in IS-QZSS-200, section 4.1.2.4.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class SatelliteEphemerisTime implements Parcelable {
+ /** The issue of ephemeris data. */
+ private final int mIode;
+
+ /** The satellite week number without rollover. */
+ private final int mWeekNumber;
+
+ /** The broadcast time of ephemeris in GNSS time of week in seconds. */
+ private final int mToeSeconds;
+
+ private SatelliteEphemerisTime(Builder builder) {
+ Preconditions.checkArgumentInRange(builder.mIode, 0, 1023, "Iode");
+ Preconditions.checkArgument(builder.mWeekNumber >= 0);
+ Preconditions.checkArgumentInRange(builder.mToeSeconds, 0, 604799, "ToeSeconds");
+ mIode = builder.mIode;
+ mWeekNumber = builder.mWeekNumber;
+ mToeSeconds = builder.mToeSeconds;
+ }
+
+ /** Returns the issue of ephemeris data. */
+ @IntRange(from = 0, to = 1023)
+ public int getIode() {
+ return mIode;
+ }
+
+ /** Returns the satellite week number without rollover. */
+ @IntRange(from = 0)
+ public int getWeekNumber() {
+ return mWeekNumber;
+ }
+
+ /** Returns the broadcast time of ephemeris in GNSS time of week in seconds. */
+ @IntRange(from = 0, to = 604799)
+ public int getToeSeconds() {
+ return mToeSeconds;
+ }
+
+ public static final @NonNull Creator<SatelliteEphemerisTime> CREATOR =
+ new Creator<SatelliteEphemerisTime>() {
+ @Override
+ public SatelliteEphemerisTime createFromParcel(Parcel in) {
+ final SatelliteEphemerisTime.Builder satelliteEphemerisTime =
+ new Builder()
+ .setIode(in.readInt())
+ .setWeekNumber(in.readInt())
+ .setToeSeconds(in.readInt());
+ return satelliteEphemerisTime.build();
+ }
+
+ @Override
+ public SatelliteEphemerisTime[] newArray(int size) {
+ return new SatelliteEphemerisTime[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mIode);
+ parcel.writeInt(mWeekNumber);
+ parcel.writeInt(mToeSeconds);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("SatelliteEphemerisTime[");
+ builder.append("iode = ").append(mIode);
+ builder.append(", weekNumber = ").append(mWeekNumber);
+ builder.append(", toeSeconds = ").append(mToeSeconds);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** Builder for {@link SatelliteEphemerisTime}. */
+ public static final class Builder {
+ private int mIode;
+ private int mWeekNumber;
+ private int mToeSeconds;
+
+ /** Sets the issue of ephemeris data. */
+ @NonNull
+ public Builder setIode(@IntRange(from = 0, to = 1023) int iode) {
+ mIode = iode;
+ return this;
+ }
+
+ /** Sets the satellite week number without rollover. */
+ @NonNull
+ public Builder setWeekNumber(@IntRange(from = 0) int weekNumber) {
+ mWeekNumber = weekNumber;
+ return this;
+ }
+
+ /** Sets the broadcast time of ephemeris in GNSS time of week in seconds. */
+ @NonNull
+ public Builder setToeSeconds(@IntRange(from = 0, to = 604799) int toeSeconds) {
+ mToeSeconds = toeSeconds;
+ return this;
+ }
+
+ /** Builds a {@link SatelliteEphemerisTime} instance as specified by this builder. */
+ @NonNull
+ public SatelliteEphemerisTime build() {
+ return new SatelliteEphemerisTime(this);
+ }
+ }
+}
diff --git a/location/java/android/location/TimeModel.java b/location/java/android/location/TimeModel.java
new file mode 100644
index 0000000..380f7b8
--- /dev/null
+++ b/location/java/android/location/TimeModel.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.GnssStatus.ConstellationType;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class contains the GNSS-GNSS system time offset between the GNSS system time.
+ *
+ * <p>This is defined in IS-GPS-200 section 30.3.3.8.2.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class TimeModel implements Parcelable {
+ /*
+ * Model represents parameters to convert from current GNSS to GNSS system
+ * time indicated by toGnss.
+ */
+ private final @ConstellationType int mToGnss;
+
+ /** Bias coefficient of GNSS time scale relative to GNSS time scale in seconds. */
+ private final double mA0;
+
+ /** Drift coefficient of GNSS time scale relative to GNSS time scale in seconds per second. */
+ private final double mA1;
+
+ /** GNSS time of week in seconds. */
+ private final int mTimeOfWeek;
+
+ /** Week number of the GNSS time. */
+ private final int mWeekNumber;
+
+ private TimeModel(Builder builder) {
+ Preconditions.checkArgumentInRange(
+ builder.mToGnss,
+ GnssStatus.CONSTELLATION_UNKNOWN,
+ GnssStatus.CONSTELLATION_COUNT,
+ "ToGnss");
+ Preconditions.checkArgumentInRange(builder.mA0, -1.0f, 1.0f, "A0");
+ Preconditions.checkArgumentInRange(builder.mA1, -3.28e-6f, 3.28e-6f, "A1");
+ Preconditions.checkArgumentInRange(builder.mTimeOfWeek, 0, 604800, "TimeOfWeek");
+ Preconditions.checkArgument(builder.mWeekNumber >= 0);
+ mToGnss = builder.mToGnss;
+ mA0 = builder.mA0;
+ mA1 = builder.mA1;
+ mTimeOfWeek = builder.mTimeOfWeek;
+ mWeekNumber = builder.mWeekNumber;
+ }
+
+ /** Returns the constellation type to convert from current GNSS system time. */
+ @ConstellationType
+ public int getToGnss() {
+ return mToGnss;
+ }
+
+ /** Returns the bias coefficient of GNSS time scale relative to GNSS time scale in seconds. */
+ @FloatRange(from = -1.0f, to = 1.0f)
+ public double getA0() {
+ return mA0;
+ }
+
+ /**
+ * Returns the drift coefficient of GNSS time scale relative to GNSS time scale in seconds per
+ * second.
+ */
+ @FloatRange(from = -3.28e-6f, to = 3.28e-6f)
+ public double getA1() {
+ return mA1;
+ }
+
+ /** Returns the GNSS time of week in seconds. */
+ @IntRange(from = 0, to = 604800)
+ public int getTimeOfWeek() {
+ return mTimeOfWeek;
+ }
+
+ /** Returns the week number of the GNSS time. */
+ @IntRange(from = 0)
+ public int getWeekNumber() {
+ return mWeekNumber;
+ }
+
+ public static final @NonNull Creator<TimeModel> CREATOR =
+ new Creator<TimeModel>() {
+ @Override
+ public TimeModel createFromParcel(@NonNull Parcel source) {
+ return new TimeModel.Builder()
+ .setToGnss(source.readInt())
+ .setA0(source.readDouble())
+ .setA1(source.readDouble())
+ .setTimeOfWeek(source.readInt())
+ .setWeekNumber(source.readInt())
+ .build();
+ }
+
+ @Override
+ public TimeModel[] newArray(int size) {
+ return new TimeModel[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("TimeModel[");
+ builder.append("toGnss = ").append(mToGnss);
+ builder.append(", a0 = ").append(mA0);
+ builder.append(", a1 = ").append(mA1);
+ builder.append(", timeOfWeek = ").append(mTimeOfWeek);
+ builder.append(", weekNumber = ").append(mWeekNumber);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mToGnss);
+ dest.writeDouble(mA0);
+ dest.writeDouble(mA1);
+ dest.writeInt(mTimeOfWeek);
+ dest.writeInt(mWeekNumber);
+ }
+
+ /** Builder for {@link TimeModel} */
+ public static final class Builder {
+
+ private @ConstellationType int mToGnss;
+ private double mA0;
+ private double mA1;
+ private int mTimeOfWeek;
+ private int mWeekNumber;
+
+ /** Sets the constellation type to convert from current GNSS system time. */
+ @NonNull
+ public Builder setToGnss(@ConstellationType int toGnss) {
+ mToGnss = toGnss;
+ return this;
+ }
+
+ /** Sets the bias coefficient of GNSS time scale relative to GNSS time scale in seconds. */
+ @NonNull
+ public Builder setA0(@FloatRange(from = -1.0f, to = 1.0f) double a0) {
+ mA0 = a0;
+ return this;
+ }
+
+ /**
+ * Sets the drift coefficient of GNSS time scale relative to GNSS time scale in seconds per
+ * second.
+ */
+ @NonNull
+ public Builder setA1(@FloatRange(from = -3.28e-6f, to = 3.28e-6f) double a1) {
+ mA1 = a1;
+ return this;
+ }
+
+ /** Sets the GNSS time of week in seconds. */
+ @NonNull
+ public Builder setTimeOfWeek(@IntRange(from = 0, to = 604800) int timeOfWeek) {
+ mTimeOfWeek = timeOfWeek;
+ return this;
+ }
+
+ /** Sets the week number of the GNSS time. */
+ @NonNull
+ public Builder setWeekNumber(@IntRange(from = 0) int weekNumber) {
+ mWeekNumber = weekNumber;
+ return this;
+ }
+
+ /** Builds the {@link TimeModel} object. */
+ @NonNull
+ public TimeModel build() {
+ return new TimeModel(this);
+ }
+ }
+}
diff --git a/location/java/android/location/UtcModel.java b/location/java/android/location/UtcModel.java
new file mode 100644
index 0000000..6dc633d
--- /dev/null
+++ b/location/java/android/location/UtcModel.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class contains parameters to convert from current GNSS time to UTC time.
+ *
+ * <p>This is defined in RINEX 3.05 "TIME SYSTEM CORR" in table A5.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public final class UtcModel implements Parcelable {
+ /** Bias coefficient of GNSS time scale relative to UTC time scale in seconds. */
+ private final double mA0;
+
+ /** Drift coefficient of GNSS time scale relative to UTC time scale in seconds per second. */
+ private final double mA1;
+
+ /** Reference GNSS time of week in seconds. */
+ private final int mTimeOfWeek;
+
+ /** Reference GNSS week number. */
+ private final int mWeekNumber;
+
+ private UtcModel(Builder builder) {
+ Preconditions.checkArgumentInRange(builder.mA0, -2.0f, 2.0f, "A0");
+ Preconditions.checkArgumentInRange(builder.mA1, -7.45e-9f, 7.45e-9f, "A1");
+ Preconditions.checkArgumentInRange(builder.mTimeOfWeek, 0, 604800, "TimeOfWeek");
+ Preconditions.checkArgument(builder.mWeekNumber >= 0);
+ mA0 = builder.mA0;
+ mA1 = builder.mA1;
+ mTimeOfWeek = builder.mTimeOfWeek;
+ mWeekNumber = builder.mWeekNumber;
+ }
+
+ /** Returns the bias coefficient of GNSS time scale relative to UTC time scale in seconds. */
+ @FloatRange(from = -2.0f, to = 2.0f)
+ public double getA0() {
+ return mA0;
+ }
+
+ /**
+ * Returns the drift coefficient of GNSS time scale relative to UTC time scale in seconds per
+ * second.
+ */
+ @FloatRange(from = -7.45e-9f, to = 7.45e-9f)
+ public double getA1() {
+ return mA1;
+ }
+
+ /** Returns the reference GNSS time of week in seconds. */
+ @IntRange(from = 0, to = 604800)
+ public int getTimeOfWeek() {
+ return mTimeOfWeek;
+ }
+
+ /** Returns the reference GNSS week number. */
+ @IntRange(from = 0)
+ public int getWeekNumber() {
+ return mWeekNumber;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+ @Override
+ @NonNull
+ public String toString() {
+ StringBuilder builder = new StringBuilder("UtcModel[");
+ builder.append("a0 = ").append(mA0);
+ builder.append(", a1 = ").append(mA1);
+ builder.append(", timeOfWeek = ").append(mTimeOfWeek);
+ builder.append(", weekNumber = ").append(mWeekNumber);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeDouble(mA0);
+ dest.writeDouble(mA1);
+ dest.writeInt(mTimeOfWeek);
+ dest.writeInt(mWeekNumber);
+ }
+
+ public static final @NonNull Creator<UtcModel> CREATOR =
+ new Creator<UtcModel>() {
+ @Override
+ public UtcModel createFromParcel(@NonNull Parcel source) {
+ return new UtcModel.Builder()
+ .setA0(source.readDouble())
+ .setA1(source.readDouble())
+ .setTimeOfWeek(source.readInt())
+ .setWeekNumber(source.readInt())
+ .build();
+ }
+
+ @Override
+ public UtcModel[] newArray(int size) {
+ return new UtcModel[size];
+ }
+ };
+
+ /** Builder for {@link UtcModel}. */
+ public static final class Builder {
+ private double mA0;
+ private double mA1;
+ private int mTimeOfWeek;
+ private int mWeekNumber;
+
+ /** Sets the bias coefficient of GNSS time scale relative to UTC time scale in seconds. */
+ @NonNull
+ public Builder setA0(@FloatRange(from = -2.0f, to = 2.0f) double a0) {
+ mA0 = a0;
+ return this;
+ }
+
+ /**
+ * Sets the drift coefficient of GNSS time scale relative to UTC time scale in seconds per
+ * second.
+ */
+ @NonNull
+ public Builder setA1(@FloatRange(from = -7.45e-9f, to = 7.45e-9f) double a1) {
+ mA1 = a1;
+ return this;
+ }
+
+ /** Sets the reference GNSS time of week in seconds. */
+ @NonNull
+ public Builder setTimeOfWeek(@IntRange(from = 0, to = 604800) int timeOfWeek) {
+ mTimeOfWeek = timeOfWeek;
+ return this;
+ }
+
+ /** Sets the reference GNSS week number. */
+ @NonNull
+ public Builder setWeekNumber(@IntRange(from = 0) int weekNumber) {
+ mWeekNumber = weekNumber;
+ return this;
+ }
+
+ /** Builds a {@link UtcModel} instance as specified by this builder. */
+ @NonNull
+ public UtcModel build() {
+ return new UtcModel(this);
+ }
+ }
+}
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index 5395206..c02cc80 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -161,3 +161,10 @@
description: "Flag for gating the density-based coarse locations"
bug: "376198890"
}
+
+flag {
+ name: "gnss_assistance_interface"
+ namespace: "location"
+ description: "Flag for GNSS assistance interface"
+ bug: "209078566"
+}
\ No newline at end of file
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index b41f40f..5689df0 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -548,8 +548,45 @@
return counts;
}
+ /** @hide */
+ @IntDef(flag = true, prefix = "AudioFormat.CHANNEL_OUT_", value = {
+ AudioFormat.CHANNEL_INVALID,
+ AudioFormat.CHANNEL_OUT_DEFAULT,
+ AudioFormat.CHANNEL_OUT_FRONT_LEFT,
+ AudioFormat.CHANNEL_OUT_FRONT_RIGHT,
+ AudioFormat.CHANNEL_OUT_FRONT_CENTER,
+ AudioFormat.CHANNEL_OUT_LOW_FREQUENCY,
+ AudioFormat.CHANNEL_OUT_BACK_LEFT,
+ AudioFormat.CHANNEL_OUT_BACK_RIGHT,
+ AudioFormat.CHANNEL_OUT_FRONT_LEFT_OF_CENTER,
+ AudioFormat.CHANNEL_OUT_FRONT_RIGHT_OF_CENTER,
+ AudioFormat.CHANNEL_OUT_BACK_CENTER,
+ AudioFormat.CHANNEL_OUT_SIDE_LEFT,
+ AudioFormat.CHANNEL_OUT_SIDE_RIGHT,
+ AudioFormat.CHANNEL_OUT_TOP_CENTER,
+ AudioFormat.CHANNEL_OUT_TOP_FRONT_LEFT,
+ AudioFormat.CHANNEL_OUT_TOP_FRONT_CENTER,
+ AudioFormat.CHANNEL_OUT_TOP_FRONT_RIGHT,
+ AudioFormat.CHANNEL_OUT_TOP_BACK_LEFT,
+ AudioFormat.CHANNEL_OUT_TOP_BACK_CENTER,
+ AudioFormat.CHANNEL_OUT_TOP_BACK_RIGHT,
+ AudioFormat.CHANNEL_OUT_TOP_SIDE_LEFT,
+ AudioFormat.CHANNEL_OUT_TOP_SIDE_RIGHT,
+ AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_LEFT,
+ AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_CENTER,
+ AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_RIGHT,
+ AudioFormat.CHANNEL_OUT_LOW_FREQUENCY_2,
+ AudioFormat.CHANNEL_OUT_FRONT_WIDE_LEFT,
+ AudioFormat.CHANNEL_OUT_FRONT_WIDE_RIGHT}
+ )
+ @Retention(RetentionPolicy.SOURCE)
+ @FlaggedApi(FLAG_SPEAKER_LAYOUT_API)
+ public @interface SpeakerLayoutChannelMask {}
+
/**
- * @return A ChannelMask representing the physical output speaker layout of the device.
+ * @return A ChannelMask representing the speaker layout of a TYPE_BUILTIN_SPEAKER device.
+ *
+ * Valid only for speakers built-in to the device.
*
* The layout channel mask only indicates which speaker channels are present, the
* physical layout of the speakers should be informed by a standard for multi-channel
@@ -557,6 +594,7 @@
* @see AudioFormat
*/
@FlaggedApi(FLAG_SPEAKER_LAYOUT_API)
+ @SpeakerLayoutChannelMask
public int getSpeakerLayoutChannelMask() {
return mPort.speakerLayoutChannelMask();
}
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index c72a74e..8bc66a0 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -17,6 +17,7 @@
package android.media;
import static android.media.audio.Flags.FLAG_DOLBY_AC4_LEVEL4_ENCODING_API;
+import static android.media.audio.Flags.FLAG_IAMF_DEFINITIONS_API;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -382,6 +383,103 @@
@FlaggedApi(FLAG_DOLBY_AC4_LEVEL4_ENCODING_API)
public static final int ENCODING_AC4_L4 = 32;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-simple">simple profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in OPUS.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_SIMPLE_PROFILE_OPUS = 33;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-simple">simple profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in AAC.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_SIMPLE_PROFILE_AAC = 34;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-simple">simple profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in FLAC.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_SIMPLE_PROFILE_FLAC = 35;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-simple">simple profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in PCM.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_SIMPLE_PROFILE_PCM = 36;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-base">base profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in OPUS.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_BASE_PROFILE_OPUS = 37;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-base">base profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in AAC.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_BASE_PROFILE_AAC = 38;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-base">base profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in FLAC.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_BASE_PROFILE_FLAC = 39;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-base">base profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in PCM.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_BASE_PROFILE_PCM = 40;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-base-enhanced">base-enhanced profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in OPUS.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS = 41;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-base-enhanced">base-enhanced profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in AAC.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC = 42;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-base-enhanced">base-enhanced profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in FLAC.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC = 43;
+ /**
+ * Audio data format: IAMF using the
+ * <a href="https://aomediacodec.github.io/iamf/#profiles-base-enhanced">base-enhanced profile</a>
+ * with audio streams <a href="https://aomediacodec.github.io/iamf/#codec_id">encoded</a>
+ * in PCM.
+ */
+ @FlaggedApi(FLAG_IAMF_DEFINITIONS_API)
+ public static final int ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM = 44;
+
/** @hide */
public static String toLogFriendlyEncoding(int enc) {
switch(enc) {
@@ -449,6 +547,30 @@
return "ENCODING_DTS_UHD_P2";
case ENCODING_DSD:
return "ENCODING_DSD";
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC:
+ return "ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC";
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC:
+ return "ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC";
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS:
+ return "ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS";
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM:
+ return "ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM";
+ case ENCODING_IAMF_BASE_PROFILE_AAC:
+ return "ENCODING_IAMF_BASE_PROFILE_AAC";
+ case ENCODING_IAMF_BASE_PROFILE_FLAC:
+ return "ENCODING_IAMF_BASE_PROFILE_FLAC";
+ case ENCODING_IAMF_BASE_PROFILE_OPUS:
+ return "ENCODING_IAMF_BASE_PROFILE_OPUS";
+ case ENCODING_IAMF_BASE_PROFILE_PCM:
+ return "ENCODING_IAMF_BASE_PROFILE_PCM";
+ case ENCODING_IAMF_SIMPLE_PROFILE_AAC:
+ return "ENCODING_IAMF_SIMPLE_PROFILE_AAC";
+ case ENCODING_IAMF_SIMPLE_PROFILE_FLAC:
+ return "ENCODING_IAMF_SIMPLE_PROFILE_FLAC";
+ case ENCODING_IAMF_SIMPLE_PROFILE_OPUS:
+ return "ENCODING_IAMF_SIMPLE_PROFILE_OPUS";
+ case ENCODING_IAMF_SIMPLE_PROFILE_PCM:
+ return "ENCODING_IAMF_SIMPLE_PROFILE_PCM";
default :
return "invalid encoding " + enc;
}
@@ -931,6 +1053,18 @@
case ENCODING_DTS_HD_MA:
case ENCODING_DTS_UHD_P2:
case ENCODING_DSD:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM:
+ case ENCODING_IAMF_BASE_PROFILE_AAC:
+ case ENCODING_IAMF_BASE_PROFILE_FLAC:
+ case ENCODING_IAMF_BASE_PROFILE_OPUS:
+ case ENCODING_IAMF_BASE_PROFILE_PCM:
+ case ENCODING_IAMF_SIMPLE_PROFILE_AAC:
+ case ENCODING_IAMF_SIMPLE_PROFILE_FLAC:
+ case ENCODING_IAMF_SIMPLE_PROFILE_OPUS:
+ case ENCODING_IAMF_SIMPLE_PROFILE_PCM:
return true;
default:
return false;
@@ -972,6 +1106,18 @@
case ENCODING_DTS_HD_MA:
case ENCODING_DTS_UHD_P2:
case ENCODING_DSD:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM:
+ case ENCODING_IAMF_BASE_PROFILE_AAC:
+ case ENCODING_IAMF_BASE_PROFILE_FLAC:
+ case ENCODING_IAMF_BASE_PROFILE_OPUS:
+ case ENCODING_IAMF_BASE_PROFILE_PCM:
+ case ENCODING_IAMF_SIMPLE_PROFILE_AAC:
+ case ENCODING_IAMF_SIMPLE_PROFILE_FLAC:
+ case ENCODING_IAMF_SIMPLE_PROFILE_OPUS:
+ case ENCODING_IAMF_SIMPLE_PROFILE_PCM:
return true;
default:
return false;
@@ -1015,6 +1161,18 @@
case ENCODING_DRA:
case ENCODING_DTS_HD_MA:
case ENCODING_DTS_UHD_P2:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM: // PCM but inside compressed stream
+ case ENCODING_IAMF_BASE_PROFILE_AAC:
+ case ENCODING_IAMF_BASE_PROFILE_FLAC:
+ case ENCODING_IAMF_BASE_PROFILE_OPUS:
+ case ENCODING_IAMF_BASE_PROFILE_PCM: // PCM but inside compressed stream
+ case ENCODING_IAMF_SIMPLE_PROFILE_AAC:
+ case ENCODING_IAMF_SIMPLE_PROFILE_FLAC:
+ case ENCODING_IAMF_SIMPLE_PROFILE_OPUS:
+ case ENCODING_IAMF_SIMPLE_PROFILE_PCM: // PCM but inside compressed stream
return false;
case ENCODING_INVALID:
default:
@@ -1058,6 +1216,18 @@
case ENCODING_DRA:
case ENCODING_DTS_HD_MA:
case ENCODING_DTS_UHD_P2:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM:
+ case ENCODING_IAMF_BASE_PROFILE_AAC:
+ case ENCODING_IAMF_BASE_PROFILE_FLAC:
+ case ENCODING_IAMF_BASE_PROFILE_OPUS:
+ case ENCODING_IAMF_BASE_PROFILE_PCM:
+ case ENCODING_IAMF_SIMPLE_PROFILE_AAC:
+ case ENCODING_IAMF_SIMPLE_PROFILE_FLAC:
+ case ENCODING_IAMF_SIMPLE_PROFILE_OPUS:
+ case ENCODING_IAMF_SIMPLE_PROFILE_PCM:
return false;
case ENCODING_INVALID:
default:
@@ -1350,6 +1520,18 @@
case ENCODING_DTS_HD_MA:
case ENCODING_DTS_UHD_P2:
case ENCODING_DSD:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS:
+ case ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM:
+ case ENCODING_IAMF_BASE_PROFILE_AAC:
+ case ENCODING_IAMF_BASE_PROFILE_FLAC:
+ case ENCODING_IAMF_BASE_PROFILE_OPUS:
+ case ENCODING_IAMF_BASE_PROFILE_PCM:
+ case ENCODING_IAMF_SIMPLE_PROFILE_AAC:
+ case ENCODING_IAMF_SIMPLE_PROFILE_FLAC:
+ case ENCODING_IAMF_SIMPLE_PROFILE_OPUS:
+ case ENCODING_IAMF_SIMPLE_PROFILE_PCM:
mEncoding = encoding;
break;
case ENCODING_INVALID:
@@ -1580,7 +1762,19 @@
ENCODING_DRA,
ENCODING_DTS_HD_MA,
ENCODING_DTS_UHD_P2,
- ENCODING_DSD }
+ ENCODING_DSD,
+ ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC,
+ ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC,
+ ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS,
+ ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM,
+ ENCODING_IAMF_BASE_PROFILE_AAC,
+ ENCODING_IAMF_BASE_PROFILE_FLAC,
+ ENCODING_IAMF_BASE_PROFILE_OPUS,
+ ENCODING_IAMF_BASE_PROFILE_PCM,
+ ENCODING_IAMF_SIMPLE_PROFILE_AAC,
+ ENCODING_IAMF_SIMPLE_PROFILE_FLAC,
+ ENCODING_IAMF_SIMPLE_PROFILE_OPUS,
+ ENCODING_IAMF_SIMPLE_PROFILE_PCM }
)
@Retention(RetentionPolicy.SOURCE)
public @interface Encoding {}
@@ -1619,7 +1813,19 @@
ENCODING_DRA,
ENCODING_DTS_HD_MA,
ENCODING_DTS_UHD_P2,
- ENCODING_DSD }
+ ENCODING_DSD,
+ ENCODING_IAMF_BASE_ENHANCED_PROFILE_AAC,
+ ENCODING_IAMF_BASE_ENHANCED_PROFILE_FLAC,
+ ENCODING_IAMF_BASE_ENHANCED_PROFILE_OPUS,
+ ENCODING_IAMF_BASE_ENHANCED_PROFILE_PCM,
+ ENCODING_IAMF_BASE_PROFILE_AAC,
+ ENCODING_IAMF_BASE_PROFILE_FLAC,
+ ENCODING_IAMF_BASE_PROFILE_OPUS,
+ ENCODING_IAMF_BASE_PROFILE_PCM,
+ ENCODING_IAMF_SIMPLE_PROFILE_AAC,
+ ENCODING_IAMF_SIMPLE_PROFILE_FLAC,
+ ENCODING_IAMF_SIMPLE_PROFILE_OPUS,
+ ENCODING_IAMF_SIMPLE_PROFILE_PCM }
)
@Retention(RetentionPolicy.SOURCE)
public @interface EncodingCanBeInvalid {}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index d0d91ba..12d7f33 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -559,6 +559,30 @@
return "AUDIO_FORMAT_MPEGH_SUB_LC_L3";
case /* AUDIO_FORMAT_MPEGH_SUB_LC_L4 */ 0x2C000024:
return "AUDIO_FORMAT_MPEGH_SUB_LC_L4";
+ case /* AUDIO_FORMAT_IAMF_SIMPLE_OPUS */ 0x34010001:
+ return "AUDIO_FORMAT_IAMF_SIMPLE_OPUS";
+ case /* AUDIO_FORMAT_IAMF_SIMPLE_AAC */ 0x34010002:
+ return "AUDIO_FORMAT_IAMF_SIMPLE_AAC";
+ case /* AUDIO_FORMAT_IAMF_SIMPLE_FLAC */ 0x34010004:
+ return "AUDIO_FORMAT_IAMF_SIMPLE_FLAC";
+ case /* AUDIO_FORMAT_IAMF_SIMPLE_PCM */ 0x34010008:
+ return "AUDIO_FORMAT_IAMF_SIMPLE_PCM";
+ case /* AUDIO_FORMAT_IAMF_BASE_OPUS */ 0x34020001:
+ return "AUDIO_FORMAT_IAMF_BASE_OPUS";
+ case /* AUDIO_FORMAT_IAMF_BASE_AAC */ 0x34020002:
+ return "AUDIO_FORMAT_IAMF_BASE_AAC";
+ case /* AUDIO_FORMAT_IAMF_BASE_FLAC */ 0x34020004:
+ return "AUDIO_FORMAT_IAMF_BASE_FLAC";
+ case /* AUDIO_FORMAT_IAMF_BASE_PCM */ 0x34020008:
+ return "AUDIO_FORMAT_IAMF_BASE_PCM";
+ case /* AUDIO_FORMAT_IAMF_BASE_ENHANCED_OPUS */ 0x34040001:
+ return "AUDIO_FORMAT_IAMF_BASE_ENHANCED_OPUS";
+ case /* AUDIO_FORMAT_IAMF_BASE_ENHANCED_AAC */ 0x34040002:
+ return "AUDIO_FORMAT_IAMF_BASE_ENHANCED_AAC";
+ case /* AUDIO_FORMAT_IAMF_BASE_ENHANCED_FLAC */ 0x34040004:
+ return "AUDIO_FORMAT_IAMF_BASE_ENHANCED_FLAC";
+ case /* AUDIO_FORMAT_IAMF_BASE_ENHANCED_PCM */ 0x34040008:
+ return "AUDIO_FORMAT_IAMF_BASE_ENHANCED_PCM";
default:
return "AUDIO_FORMAT_(" + audioFormat + ")";
}
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index dd5067a..a3ad340 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -49,6 +49,7 @@
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* Describes the properties of a route.
@@ -189,6 +190,7 @@
* the device.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_BUILTIN_SPEAKER
*/
public static final int TYPE_BUILTIN_SPEAKER = AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
@@ -196,6 +198,7 @@
* Indicates the route is a headset, which is the combination of a headphones and a microphone.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_WIRED_HEADSET
*/
public static final int TYPE_WIRED_HEADSET = AudioDeviceInfo.TYPE_WIRED_HEADSET;
@@ -203,6 +206,7 @@
* Indicates the route is a pair of wired headphones.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_WIRED_HEADPHONES
*/
public static final int TYPE_WIRED_HEADPHONES = AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
@@ -210,6 +214,7 @@
* Indicates the route is a bluetooth device, such as a bluetooth speaker or headphones.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_BLUETOOTH_A2DP
*/
public static final int TYPE_BLUETOOTH_A2DP = AudioDeviceInfo.TYPE_BLUETOOTH_A2DP;
@@ -217,6 +222,7 @@
* Indicates the route is an HDMI connection.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_HDMI
*/
public static final int TYPE_HDMI = AudioDeviceInfo.TYPE_HDMI;
@@ -224,6 +230,7 @@
* Indicates the route is an Audio Return Channel of an HDMI connection.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_HDMI_ARC
*/
@FlaggedApi(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
public static final int TYPE_HDMI_ARC = AudioDeviceInfo.TYPE_HDMI_ARC;
@@ -232,24 +239,34 @@
* Indicates the route is an Enhanced Audio Return Channel of an HDMI connection.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_HDMI_EARC
*/
@FlaggedApi(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
public static final int TYPE_HDMI_EARC = AudioDeviceInfo.TYPE_HDMI_EARC;
/**
* Indicates the route is a digital line connection (for example S/PDIF).
+ *
+ * @see #getType
+ * @see AudioDeviceInfo#TYPE_LINE_DIGITAL
*/
@FlaggedApi(FLAG_ENABLE_NEW_WIRED_MEDIA_ROUTE_2_INFO_TYPES)
public static final int TYPE_LINE_DIGITAL = AudioDeviceInfo.TYPE_LINE_DIGITAL;
/**
* Indicates the route is an analog line-level connection.
+ *
+ * @see #getType
+ * @see AudioDeviceInfo#TYPE_LINE_ANALOG
*/
@FlaggedApi(FLAG_ENABLE_NEW_WIRED_MEDIA_ROUTE_2_INFO_TYPES)
public static final int TYPE_LINE_ANALOG = AudioDeviceInfo.TYPE_LINE_ANALOG;
/**
* Indicates the route is using the auxiliary line-level connectors.
+ *
+ * @see #getType
+ * @see AudioDeviceInfo#TYPE_AUX_LINE
*/
@FlaggedApi(FLAG_ENABLE_NEW_WIRED_MEDIA_ROUTE_2_INFO_TYPES)
public static final int TYPE_AUX_LINE = AudioDeviceInfo.TYPE_AUX_LINE;
@@ -258,6 +275,7 @@
* Indicates the route is a USB audio device.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_USB_DEVICE
*/
public static final int TYPE_USB_DEVICE = AudioDeviceInfo.TYPE_USB_DEVICE;
@@ -265,6 +283,7 @@
* Indicates the route is a USB audio device in accessory mode.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_USB_ACCESSORY
*/
public static final int TYPE_USB_ACCESSORY = AudioDeviceInfo.TYPE_USB_ACCESSORY;
@@ -272,6 +291,7 @@
* Indicates the route is the audio device associated with a dock.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_DOCK
*/
public static final int TYPE_DOCK = AudioDeviceInfo.TYPE_DOCK;
@@ -279,6 +299,7 @@
* Indicates the route is a USB audio headset.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_USB_HEADSET
*/
public static final int TYPE_USB_HEADSET = AudioDeviceInfo.TYPE_USB_HEADSET;
@@ -286,6 +307,7 @@
* Indicates the route is a hearing aid.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_HEARING_AID
*/
public static final int TYPE_HEARING_AID = AudioDeviceInfo.TYPE_HEARING_AID;
@@ -293,6 +315,7 @@
* Indicates the route is a Bluetooth Low Energy (BLE) HEADSET.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_BLE_HEADSET
*/
public static final int TYPE_BLE_HEADSET = AudioDeviceInfo.TYPE_BLE_HEADSET;
@@ -304,6 +327,7 @@
* to provide a better experience on multichannel contents.
*
* @see #getType
+ * @see AudioDeviceInfo#TYPE_MULTICHANNEL_GROUP
*/
@FlaggedApi(FLAG_ENABLE_MULTICHANNEL_GROUP_DEVICE)
public static final int TYPE_MULTICHANNEL_SPEAKER_GROUP =
@@ -617,7 +641,7 @@
private final String mProviderId;
private final boolean mIsVisibilityRestricted;
private final Set<String> mAllowedPackages;
- private final Set<String> mRequiredPermissions;
+ private final List<Set<String>> mRequiredPermissions;
@SuitabilityStatus private final int mSuitabilityStatus;
MediaRoute2Info(@NonNull Builder builder) {
@@ -642,7 +666,7 @@
mIsVisibilityRestricted = builder.mIsVisibilityRestricted;
mAllowedPackages = builder.mAllowedPackages;
mSuitabilityStatus = builder.mSuitabilityStatus;
- mRequiredPermissions = Set.copyOf(builder.mRequiredPermissions);
+ mRequiredPermissions = List.copyOf(builder.mRequiredPermissions);
}
MediaRoute2Info(@NonNull Parcel in) {
@@ -667,7 +691,12 @@
mProviderId = in.readString();
mIsVisibilityRestricted = in.readBoolean();
mAllowedPackages = Set.of(in.createString8Array());
- mRequiredPermissions = Set.of(in.createString8Array());
+ ArrayList<Set<String>> requiredPermissions = new ArrayList<>();
+ int numRequiredPermissionSets = in.readInt();
+ for (int i = 0; i < numRequiredPermissionSets; i++) {
+ requiredPermissions.add(Set.of(in.createString8Array()));
+ }
+ mRequiredPermissions = List.copyOf(requiredPermissions); // Use copyOf to make it immutable.
mSuitabilityStatus = in.readInt();
}
@@ -911,11 +940,12 @@
}
/**
- * @return the set of permissions which must be held to see this route
+ * @return a list of permission sets - all the permissions in at least one of these sets must be
+ * held to see this route.
*/
@NonNull
@FlaggedApi(FLAG_ENABLE_ROUTE_VISIBILITY_CONTROL_API)
- public Set<String> getRequiredPermissions() {
+ public List<Set<String>> getRequiredPermissions() {
return mRequiredPermissions;
}
@@ -1096,7 +1126,8 @@
.append(", allowedPackages=")
.append(String.join(",", mAllowedPackages))
.append(", mRequiredPermissions=")
- .append(String.join(",", mRequiredPermissions))
+ .append(mRequiredPermissions.stream().map(set -> String.join(",", set)).collect(
+ Collectors.joining("),(", "(", ")")))
.append(", suitabilityStatus=")
.append(mSuitabilityStatus)
.append(" }")
@@ -1130,7 +1161,10 @@
dest.writeString(mProviderId);
dest.writeBoolean(mIsVisibilityRestricted);
dest.writeString8Array(mAllowedPackages.toArray(new String[0]));
- dest.writeString8Array(mRequiredPermissions.toArray(new String[0]));
+ dest.writeInt(mRequiredPermissions.size());
+ for (Set<String> permissionSet : mRequiredPermissions) {
+ dest.writeString8Array(permissionSet.toArray(new String[0]));
+ }
dest.writeInt(mSuitabilityStatus);
}
@@ -1279,7 +1313,7 @@
private String mProviderId;
private boolean mIsVisibilityRestricted;
private Set<String> mAllowedPackages;
- private Set<String> mRequiredPermissions;
+ private List<Set<String>> mRequiredPermissions;
@SuitabilityStatus private int mSuitabilityStatus;
/**
@@ -1305,7 +1339,7 @@
mDeduplicationIds = Set.of();
mAllowedPackages = Set.of();
mSuitabilityStatus = SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER;
- mRequiredPermissions = Set.of();
+ mRequiredPermissions = List.of();
}
/**
@@ -1587,7 +1621,7 @@
public Builder setVisibilityPublic() {
mIsVisibilityRestricted = false;
mAllowedPackages = Set.of();
- mRequiredPermissions = Set.of();
+ mRequiredPermissions = List.of();
return this;
}
@@ -1614,13 +1648,31 @@
/**
* Limits the visibility of this route to holders of a set of permissions.
*
+ * <p>Calls to this method override any previous calls of
+ * {@link #setRequiredPermissions(Set)} or {@link #setRequiredPermissions(List)}.
+ *
* @param requiredPermissions the list of all permissions which must be held in order to
* see this route.
*/
@NonNull
@FlaggedApi(FLAG_ENABLE_ROUTE_VISIBILITY_CONTROL_API)
public Builder setRequiredPermissions(@NonNull Set<String> requiredPermissions) {
- mRequiredPermissions = Set.copyOf(requiredPermissions);
+ return setRequiredPermissions(List.of(requiredPermissions));
+ }
+
+ /**
+ * Limits the visibility of this route to holders of one of a set of permissions.
+ *
+ * <p>Calls to this method override any previous calls of
+ * {@link #setRequiredPermissions(Set)} or {@link #setRequiredPermissions(List)}.
+ *
+ * @param requiresOneOf a list of Sets of permissions. Holding all permissions in at least
+ * one of the Sets is required for the route to be visible.
+ */
+ @NonNull
+ @FlaggedApi(FLAG_ENABLE_ROUTE_VISIBILITY_CONTROL_API)
+ public Builder setRequiredPermissions(@NonNull List<Set<String>> requiresOneOf) {
+ mRequiredPermissions = List.copyOf(requiresOneOf);
return this;
}
diff --git a/core/java/android/app/ondeviceintelligence/Feature.aidl b/media/java/android/media/quality/ActiveProcessingPicture.aidl
similarity index 80%
rename from core/java/android/app/ondeviceintelligence/Feature.aidl
rename to media/java/android/media/quality/ActiveProcessingPicture.aidl
index 18494d7..2851306 100644
--- a/core/java/android/app/ondeviceintelligence/Feature.aidl
+++ b/media/java/android/media/quality/ActiveProcessingPicture.aidl
@@ -5,7 +5,7 @@
* you may not use this 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,
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-package android.app.ondeviceintelligence;
+package android.media.quality;
-/**
- * @hide
- */
-parcelable Feature;
+parcelable ActiveProcessingPicture;
\ No newline at end of file
diff --git a/media/java/android/media/quality/ActiveProcessingPicture.java b/media/java/android/media/quality/ActiveProcessingPicture.java
new file mode 100644
index 0000000..e16ad62
--- /dev/null
+++ b/media/java/android/media/quality/ActiveProcessingPicture.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 android.media.quality;
+
+import android.annotation.FlaggedApi;
+import android.media.tv.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Active picture represents an image or video undergoing picture processing which uses a picture
+ * profile. The picture profile is used to configure the picture processing parameters.
+ */
+@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
+public final class ActiveProcessingPicture implements Parcelable {
+ private final int mId;
+ private final String mProfileId;
+
+ public ActiveProcessingPicture(int id, @NonNull String profileId) {
+ mId = id;
+ mProfileId = profileId;
+ }
+
+ /** @hide */
+ ActiveProcessingPicture(Parcel in) {
+ mId = in.readInt();
+ mProfileId = in.readString();
+ }
+
+ @NonNull
+ public static final Creator<ActiveProcessingPicture> CREATOR = new Creator<>() {
+ @Override
+ public ActiveProcessingPicture createFromParcel(Parcel in) {
+ return new ActiveProcessingPicture(in);
+ }
+
+ @Override
+ public ActiveProcessingPicture[] newArray(int size) {
+ return new ActiveProcessingPicture[size];
+ }
+ };
+
+ /**
+ * An ID that uniquely identifies the active content.
+ *
+ * <p>The ID is assigned by the system to distinguish different active contents.
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * The ID of the picture profile used to configure the content.
+ */
+ @NonNull
+ public String getProfileId() {
+ return mProfileId;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mId);
+ dest.writeString(mProfileId);
+ }
+}
diff --git a/media/java/android/media/quality/IMediaQualityManager.aidl b/media/java/android/media/quality/IMediaQualityManager.aidl
index 70211ca..253c2d8 100644
--- a/media/java/android/media/quality/IMediaQualityManager.aidl
+++ b/media/java/android/media/quality/IMediaQualityManager.aidl
@@ -23,51 +23,58 @@
import android.media.quality.ParamCapability;
import android.media.quality.PictureProfileHandle;
import android.media.quality.PictureProfile;
+import android.media.quality.SoundProfileHandle;
import android.media.quality.SoundProfile;
+import android.os.UserHandle;
/**
* Interface for Media Quality Manager
* @hide
*/
interface IMediaQualityManager {
- PictureProfile createPictureProfile(in PictureProfile pp, int userId);
- void updatePictureProfile(in String id, in PictureProfile pp, int userId);
- void removePictureProfile(in String id, int userId);
- PictureProfile getPictureProfile(in int type, in String name, int userId);
- List<PictureProfile> getPictureProfilesByPackage(in String packageName, int userId);
- List<PictureProfile> getAvailablePictureProfiles(int userId);
- boolean setDefaultPictureProfile(in String id, int userId);
- List<String> getPictureProfilePackageNames(int userId);
- List<String> getPictureProfileAllowList(int userId);
- void setPictureProfileAllowList(in List<String> packages, int userId);
- PictureProfileHandle getPictureProfileHandle(in String id, int userId);
+ PictureProfile createPictureProfile(in PictureProfile pp, in UserHandle user);
+ void updatePictureProfile(in String id, in PictureProfile pp, in UserHandle user);
+ void removePictureProfile(in String id, in UserHandle user);
+ boolean setDefaultPictureProfile(in String id, in UserHandle user);
+ PictureProfile getPictureProfile(
+ in int type, in String name, in boolean includeParams, in UserHandle user);
+ List<PictureProfile> getPictureProfilesByPackage(
+ in String packageName, in boolean includeParams, in UserHandle user);
+ List<PictureProfile> getAvailablePictureProfiles(in boolean includeParams, in UserHandle user);
+ List<String> getPictureProfilePackageNames(in UserHandle user);
+ List<String> getPictureProfileAllowList(in UserHandle user);
+ void setPictureProfileAllowList(in List<String> packages, in UserHandle user);
+ List<PictureProfileHandle> getPictureProfileHandle(in String[] id, in UserHandle user);
- SoundProfile createSoundProfile(in SoundProfile pp, int userId);
- void updateSoundProfile(in String id, in SoundProfile pp, int userId);
- void removeSoundProfile(in String id, int userId);
- SoundProfile getSoundProfile(in int type, in String name, int userId);
- List<SoundProfile> getSoundProfilesByPackage(in String packageName, int userId);
- List<SoundProfile> getAvailableSoundProfiles(int userId);
- boolean setDefaultSoundProfile(in String id, int userId);
- List<String> getSoundProfilePackageNames(int userId);
- List<String> getSoundProfileAllowList(int userId);
- void setSoundProfileAllowList(in List<String> packages, int userId);
+ SoundProfile createSoundProfile(in SoundProfile pp, in UserHandle user);
+ void updateSoundProfile(in String id, in SoundProfile pp, in UserHandle user);
+ void removeSoundProfile(in String id, in UserHandle user);
+ boolean setDefaultSoundProfile(in String id, in UserHandle user);
+ SoundProfile getSoundProfile(
+ in int type, in String name, in boolean includeParams, in UserHandle user);
+ List<SoundProfile> getSoundProfilesByPackage(
+ in String packageName, in boolean includeParams, in UserHandle user);
+ List<SoundProfile> getAvailableSoundProfiles(in boolean includeParams, in UserHandle user);
+ List<String> getSoundProfilePackageNames(in UserHandle user);
+ List<String> getSoundProfileAllowList(in UserHandle user);
+ void setSoundProfileAllowList(in List<String> packages, in UserHandle user);
+ List<SoundProfileHandle> getSoundProfileHandle(in String[] id, in UserHandle user);
void registerPictureProfileCallback(in IPictureProfileCallback cb);
void registerSoundProfileCallback(in ISoundProfileCallback cb);
void registerAmbientBacklightCallback(in IAmbientBacklightCallback cb);
- List<ParamCapability> getParamCapabilities(in List<String> names, int userId);
+ List<ParamCapability> getParamCapabilities(in List<String> names, in UserHandle user);
- boolean isSupported(int userId);
- void setAutoPictureQualityEnabled(in boolean enabled, int userId);
- boolean isAutoPictureQualityEnabled(int userId);
- void setSuperResolutionEnabled(in boolean enabled, int userId);
- boolean isSuperResolutionEnabled(int userId);
- void setAutoSoundQualityEnabled(in boolean enabled, int userId);
- boolean isAutoSoundQualityEnabled(int userId);
+ boolean isSupported(in UserHandle user);
+ void setAutoPictureQualityEnabled(in boolean enabled, in UserHandle user);
+ boolean isAutoPictureQualityEnabled(in UserHandle user);
+ void setSuperResolutionEnabled(in boolean enabled, in UserHandle user);
+ boolean isSuperResolutionEnabled(in UserHandle user);
+ void setAutoSoundQualityEnabled(in boolean enabled, in UserHandle user);
+ boolean isAutoSoundQualityEnabled(in UserHandle user);
- void setAmbientBacklightSettings(in AmbientBacklightSettings settings, int userId);
- void setAmbientBacklightEnabled(in boolean enabled, int userId);
- boolean isAmbientBacklightEnabled(int userId);
+ void setAmbientBacklightSettings(in AmbientBacklightSettings settings, in UserHandle user);
+ void setAmbientBacklightEnabled(in boolean enabled, in UserHandle user);
+ boolean isAmbientBacklightEnabled(in UserHandle user);
}
diff --git a/media/java/android/media/quality/IPictureProfileCallback.aidl b/media/java/android/media/quality/IPictureProfileCallback.aidl
index 34aa2b0..7071a16 100644
--- a/media/java/android/media/quality/IPictureProfileCallback.aidl
+++ b/media/java/android/media/quality/IPictureProfileCallback.aidl
@@ -29,5 +29,5 @@
void onPictureProfileUpdated(in String id, in PictureProfile p);
void onPictureProfileRemoved(in String id, in PictureProfile p);
void onParamCapabilitiesChanged(in String id, in List<ParamCapability> caps);
- void onError(in int err);
+ void onError(in String id, in int err);
}
diff --git a/media/java/android/media/quality/ISoundProfileCallback.aidl b/media/java/android/media/quality/ISoundProfileCallback.aidl
index 9043757..30bb106 100644
--- a/media/java/android/media/quality/ISoundProfileCallback.aidl
+++ b/media/java/android/media/quality/ISoundProfileCallback.aidl
@@ -29,5 +29,5 @@
void onSoundProfileUpdated(in String id, in SoundProfile p);
void onSoundProfileRemoved(in String id, in SoundProfile p);
void onParamCapabilitiesChanged(in String id, in List<ParamCapability> caps);
- void onError(in int err);
+ void onError(in String id, in int err);
}
diff --git a/media/java/android/media/quality/MediaQualityContract.java b/media/java/android/media/quality/MediaQualityContract.java
index 7b0bd04..6a52bcb 100644
--- a/media/java/android/media/quality/MediaQualityContract.java
+++ b/media/java/android/media/quality/MediaQualityContract.java
@@ -75,11 +75,9 @@
public static final String PARAMETER_SATURATION = "saturation";
/**
- * @hide
- */
- public static final String PARAMETER_COLOR = "color";
- /**
- * @hide
+ * The hue.
+ *
+ * <p>Type: INTEGER
*/
public static final String PARAMETER_HUE = "hue";
@@ -89,47 +87,77 @@
public static final String PARAMETER_BACKLIGHT = "backlight";
/**
- * @hide
+ * Adjust brightness in advance color engine. Similar to a "brightness" control on a TV
+ * but acts at a lower level.
+ *
+ * <p>Type: INTEGER
*/
public static final String PARAMETER_COLOR_TUNER_BRIGHTNESS = "color_tuner_brightness";
/**
- * @hide
+ * Adjust saturation in advance color engine. Similar to a "saturation" control on a TV
+ * but acts at a lower level.
+ *
+ * <p>Type: INTEGER
*/
public static final String PARAMETER_COLOR_TUNER_SATURATION = "color_tuner_saturation";
/**
- * @hide
+ * Adjust hue in advance color engine. Similar to a "hue" control on a TV but acts at a
+ * lower level.
+ *
+ * <p>Type: INTEGER
*/
public static final String PARAMETER_COLOR_TUNER_HUE = "color_tuner_hue";
/**
- * @hide
+ * Advance setting for red offset. Adjust the black level of red color channels, it
+ * controls the minimum intensity of each color, affecting the shadows and
+ * dark areas of the image.
+ *
+ * <p>Type: INTEGER
*/
- public static final String PARAMETER_COLOR_TUNER_REDO_FFSET = "color_tuner_red_offset";
+ public static final String PARAMETER_COLOR_TUNER_RED_OFFSET = "color_tuner_red_offset";
/**
- * @hide
+ * Advance setting for green offset. Adjust the black level of green color channels, it
+ * controls the minimum intensity of each color, affecting the shadows and dark
+ * areas of the image.
+ *
+ * <p>Type: INTEGER
*/
public static final String PARAMETER_COLOR_TUNER_GREEN_OFFSET = "color_tuner_green_offset";
/**
- * @hide
+ * Advance setting for blue offset. Adjust the black level of blue color channels, it
+ * controls the minimum intensity of each color, affecting the shadows and dark areas
+ * of the image.
+ *
+ * <p>Type: INTEGER
*/
public static final String PARAMETER_COLOR_TUNER_BLUE_OFFSET = "color_tuner_blue_offset";
/**
- * @hide
+ * Advance setting for red gain. Adjust the gain or amplification of the red color channels.
+ * They control the overall intensity and white balance of red.
+ *
+ * <p>Type: INTEGER
*/
public static final String PARAMETER_COLOR_TUNER_RED_GAIN = "color_tuner_red_gain";
/**
- * @hide
+ * Advance setting for green gain. Adjust the gain or amplification of the green color
+ * channels. They control the overall intensity and white balance of green.
+ *
+ * <p>Type: INTEGER
*/
public static final String PARAMETER_COLOR_TUNER_GREEN_GAIN = "color_tuner_green_gain";
/**
- * @hide
+ * Advance setting for blue gain. Adjust the gain or amplification of the blue color
+ * channels.They control the overall intensity and white balance of blue.
+ *
+ * <p>Type: INTEGER
*/
public static final String PARAMETER_COLOR_TUNER_BLUE_GAIN = "color_tuner_blue_gain";
@@ -143,33 +171,54 @@
*/
public static final String PARAMETER_AI_SUPER_RESOLUTION = "ai_super_resolution";
- /**
- * @hide
+ /** Noise reduction.
+ * (Off, Low, Medium, High)
+ * @see android.hardware.tv.mediaquality.QualityLevel
+ *
+ * <p>Type: STRING
*/
public static final String PARAMETER_NOISE_REDUCTION = "noise_reduction";
/**
- * @hide
- */
+ * MPEG (moving picture experts group) noise reduction
+ * (Off, Low, Medium, High)
+ * @see android.hardware.tv.mediaquality.QualityLevel
+ *
+ * <p>Type: STRING
+ * */
public static final String PARAMETER_MPEG_NOISE_REDUCTION = "mpeg_noise_reduction";
/**
- * @hide
+ * Refine the flesh colors in the pictures without affecting the other colors on the screen.
+ * (Off, Low, Medium, High)
+ * @see android.hardware.tv.mediaquality.QualityLevel
+ *
+ * <p>Type: STRING
*/
public static final String PARAMETER_FLESH_TONE = "flesh_tone";
/**
- * @hide
+ * Contour noise reduction.
+ * (Off, Low, Medium, High)
+ * @see android.hardware.tv.mediaquality.QualityLevel
+ *
+ * <p>Type: STRING
*/
public static final String PARAMETER_DECONTOUR = "decontour";
/**
- * @hide
+ * Dynamically change picture luma to enhance contrast.
+ * (Off, Low, Medium, High)
+ * @see android.hardware.tv.mediaquality.QualityLevel
+ *
+ * <p>Type: STRING
*/
public static final String PARAMETER_DYNAMIC_LUMA_CONTROL = "dynamic_luma_control";
/**
- * @hide
+ * Enable/disable film mode
+ *
+ * <p>Type: BOOLEAN
*/
public static final String PARAMETER_FILM_MODE = "film_mode";
@@ -179,25 +228,50 @@
public static final String PARAMETER_BLACK_STRETCH = "black_stretch";
/**
- * @hide
+ * Enable/disable blue color auto stretch
+ *
+ * <p>Type: BOOLEAN
*/
public static final String PARAMETER_BLUE_STRETCH = "blue_stretch";
/**
- * @hide
+ * Enable/disable the overall color tuning feature.
+ *
+ * <p>Type: BOOLEAN
*/
public static final String PARAMETER_COLOR_TUNE = "color_tune";
/**
- * @hide
+ * Adjust color temperature type
+ *
+ * <p>Type: INTEGER
*/
public static final String PARAMETER_COLOR_TEMPERATURE = "color_temperature";
/**
- * @hide
+ * Enable/disable globe dimming.
+ *
+ * <p>Type: BOOLEAN
*/
public static final String PARAMETER_GLOBAL_DIMMING = "global_dimming";
+ /**
+ * Enable/disable auto adjust picture parameter based on the TV content.
+ *
+ * <p>Type: BOOLEAN
+ */
+ public static final String PARAMETER_AUTO_PICTURE_QUALITY_ENABLED =
+ "auto_picture_quality_enabled";
+
+ /**
+ * Enable/disable auto upscaling the picture quality. It analyzes the lower-resolution
+ * image and uses its knowledge to invent the missing pixel, make the image look sharper.
+ *
+ * <p>Type: BOOLEAN
+ */
+ public static final String PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED =
+ "auto_super_resolution_enabled";
+
private PictureQuality() {
}
}
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
index 9d66086..7e87462 100644
--- a/media/java/android/media/quality/MediaQualityManager.java
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -20,11 +20,13 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.media.tv.flags.Flags;
import android.os.RemoteException;
+import android.os.UserHandle;
import androidx.annotation.RequiresPermission;
@@ -47,7 +49,7 @@
private final IMediaQualityManager mService;
private final Context mContext;
- private final int mUserId;
+ private final UserHandle mUserHandle;
private final Object mLock = new Object();
// @GuardedBy("mLock")
private final List<PictureProfileCallbackRecord> mPpCallbackRecords = new ArrayList<>();
@@ -55,6 +57,9 @@
private final List<SoundProfileCallbackRecord> mSpCallbackRecords = new ArrayList<>();
// @GuardedBy("mLock")
private final List<AmbientBacklightCallbackRecord> mAbCallbackRecords = new ArrayList<>();
+ // @GuardedBy("mLock")
+ private final List<ActiveProcessingPictureListenerRecord> mApListenerRecords =
+ new ArrayList<>();
/**
@@ -62,7 +67,7 @@
*/
public MediaQualityManager(Context context, IMediaQualityManager service) {
mContext = context;
- mUserId = context.getUserId();
+ mUserHandle = context.getUser();
mService = service;
IPictureProfileCallback ppCallback = new IPictureProfileCallback.Stub() {
@Override
@@ -102,11 +107,11 @@
}
}
@Override
- public void onError(int err) {
+ public void onError(String profileId, int err) {
synchronized (mLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
// TODO: filter callback record
- record.postError(err);
+ record.postError(profileId, err);
}
}
}
@@ -149,11 +154,11 @@
}
}
@Override
- public void onError(int err) {
+ public void onError(String profileId, int err) {
synchronized (mLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
// TODO: filter callback record
- record.postError(err);
+ record.postError(profileId, err);
}
}
}
@@ -210,18 +215,21 @@
}
}
-
/**
* Gets picture profile by given profile type and name.
*
+ * @param type the type of the profile.
+ * @param name the name of the profile.
+ * @param includeParams {@code true} to include parameters in the profile; {@code false}
+ * otherwise.
* @return the corresponding picture profile if available; {@code null} if the name doesn't
- * exist.
+ * exist.
*/
@Nullable
public PictureProfile getPictureProfile(
- @PictureProfile.ProfileType int type, @NonNull String name) {
+ @PictureProfile.ProfileType int type, @NonNull String name, boolean includeParams) {
try {
- return mService.getPictureProfile(type, name, mUserId);
+ return mService.getPictureProfile(type, name, includeParams, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -231,14 +239,18 @@
/**
* Gets profiles that available to the given package.
*
+ * @param packageName the package name of the profiles.
+ * @param includeParams {@code true} to include parameters in the profile; {@code false}
+ * otherwise.
* @hide
*/
@SystemApi
@NonNull
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
- public List<PictureProfile> getPictureProfilesByPackage(@NonNull String packageName) {
+ public List<PictureProfile> getPictureProfilesByPackage(
+ @NonNull String packageName, boolean includeParams) {
try {
- return mService.getPictureProfilesByPackage(packageName, mUserId);
+ return mService.getPictureProfilesByPackage(packageName, includeParams, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -246,11 +258,16 @@
/**
* Gets profiles that available to the caller.
+ *
+ * @param includeParams {@code true} to include parameters in the profile; {@code false}
+ * otherwise.
+ * @return the corresponding picture profile if available; {@code null} if the name doesn't
+ * exist.
*/
@NonNull
- public List<PictureProfile> getAvailablePictureProfiles() {
+ public List<PictureProfile> getAvailablePictureProfiles(boolean includeParams) {
try {
- return mService.getAvailablePictureProfiles(mUserId);
+ return mService.getAvailablePictureProfiles(includeParams, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -268,7 +285,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public boolean setDefaultPictureProfile(@Nullable String id) {
try {
- return mService.setDefaultPictureProfile(id, mUserId);
+ return mService.setDefaultPictureProfile(id, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -277,7 +294,7 @@
/**
* Gets all package names whose picture profiles are available.
*
- * @see #getPictureProfilesByPackage(String)
+ * @see #getPictureProfilesByPackage(String, boolean)
* @hide
*/
@SystemApi
@@ -285,7 +302,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public List<String> getPictureProfilePackageNames() {
try {
- return mService.getPictureProfilePackageNames(mUserId);
+ return mService.getPictureProfilePackageNames(mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -295,9 +312,21 @@
* Gets picture profile handle by profile ID.
* @hide
*/
- public PictureProfileHandle getPictureProfileHandle(String id) {
+ public List<PictureProfileHandle> getPictureProfileHandle(String[] id) {
try {
- return mService.getPictureProfileHandle(id, mUserId);
+ return mService.getPictureProfileHandle(id, mUserHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets sound profile handle by profile ID.
+ * @hide
+ */
+ public List<SoundProfileHandle> getSoundProfileHandle(String[] id) {
+ try {
+ return mService.getSoundProfileHandle(id, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -308,10 +337,12 @@
*
* <p>If the profile is created successfully,
* {@link PictureProfileCallback#onPictureProfileAdded(String, PictureProfile)} is invoked.
+ *
+ * @param pp the {@link PictureProfile} object to be created.
*/
public void createPictureProfile(@NonNull PictureProfile pp) {
try {
- mService.createPictureProfile(pp, mUserId);
+ mService.createPictureProfile(pp, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -320,10 +351,13 @@
/**
* Updates an existing picture profile and store it in the system.
+ *
+ * @param profileId the id of the object to be updated.
+ * @param pp the {@link PictureProfile} object to be updated.
*/
public void updatePictureProfile(@NonNull String profileId, @NonNull PictureProfile pp) {
try {
- mService.updatePictureProfile(profileId, pp, mUserId);
+ mService.updatePictureProfile(profileId, pp, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -332,10 +366,12 @@
/**
* Removes a picture profile from the system.
+ *
+ * @param profileId the id of the object to be removed.
*/
public void removePictureProfile(@NonNull String profileId) {
try {
- mService.removePictureProfile(profileId, mUserId);
+ mService.removePictureProfile(profileId, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -371,18 +407,20 @@
}
}
-
/**
* Gets sound profile by given profile type and name.
*
- * @return the corresponding sound profile if available; {@code null} if the name doesn't
- * exist.
+ * @param type the type of the profile.
+ * @param name the name of the profile.
+ * @param includeParams {@code true} to include parameters in the profile; {@code false}
+ * otherwise.
+ * @return the corresponding sound profile if available; {@code null} if the name doesn't exist.
*/
@Nullable
public SoundProfile getSoundProfile(
- @SoundProfile.ProfileType int type, @NonNull String name) {
+ @SoundProfile.ProfileType int type, @NonNull String name, boolean includeParams) {
try {
- return mService.getSoundProfile(type, name, mUserId);
+ return mService.getSoundProfile(type, name, includeParams, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -392,14 +430,18 @@
/**
* Gets profiles that available to the given package.
*
+ * @param packageName the package name of the profiles.
+ * @param includeParams {@code true} to include parameters in the profile; {@code false}
+ * otherwise.
* @hide
*/
@SystemApi
@NonNull
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
- public List<SoundProfile> getSoundProfilesByPackage(@NonNull String packageName) {
+ public List<SoundProfile> getSoundProfilesByPackage(
+ @NonNull String packageName, boolean includeParams) {
try {
- return mService.getSoundProfilesByPackage(packageName, mUserId);
+ return mService.getSoundProfilesByPackage(packageName, includeParams, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -407,11 +449,16 @@
/**
* Gets profiles that available to the caller package.
+ *
+ * @param includeParams {@code true} to include parameters in the profile; {@code false}
+ * otherwise.
+ *
+ * @return the corresponding sound profile if available; {@code null} if the none available.
*/
@NonNull
- public List<SoundProfile> getAvailableSoundProfiles() {
+ public List<SoundProfile> getAvailableSoundProfiles(boolean includeParams) {
try {
- return mService.getAvailableSoundProfiles(mUserId);
+ return mService.getAvailableSoundProfiles(includeParams, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -429,7 +476,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
public boolean setDefaultSoundProfile(@Nullable String id) {
try {
- return mService.setDefaultSoundProfile(id, mUserId);
+ return mService.setDefaultSoundProfile(id, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -438,7 +485,7 @@
/**
* Gets all package names whose sound profiles are available.
*
- * @see #getSoundProfilesByPackage(String)
+ * @see #getSoundProfilesByPackage(String, boolean)
*
* @hide
*/
@@ -447,7 +494,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
public List<String> getSoundProfilePackageNames() {
try {
- return mService.getSoundProfilePackageNames(mUserId);
+ return mService.getSoundProfilePackageNames(mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -459,10 +506,12 @@
*
* <p>If the profile is created successfully,
* {@link SoundProfileCallback#onSoundProfileAdded(String, SoundProfile)} is invoked.
+ *
+ * @param sp the {@link SoundProfile} object to be created.
*/
public void createSoundProfile(@NonNull SoundProfile sp) {
try {
- mService.createSoundProfile(sp, mUserId);
+ mService.createSoundProfile(sp, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -471,10 +520,13 @@
/**
* Updates an existing sound profile and store it in the system.
+ *
+ * @param profileId the id of the object to be updated.
+ * @param sp the {@link SoundProfile} object to be updated.
*/
public void updateSoundProfile(@NonNull String profileId, @NonNull SoundProfile sp) {
try {
- mService.updateSoundProfile(profileId, sp, mUserId);
+ mService.updateSoundProfile(profileId, sp, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -483,10 +535,12 @@
/**
* Removes a sound profile from the system.
+ *
+ * @param profileId the id of the object to be removed.
*/
public void removeSoundProfile(@NonNull String profileId) {
try {
- mService.removeSoundProfile(profileId, mUserId);
+ mService.removeSoundProfile(profileId, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -498,7 +552,7 @@
@NonNull
public List<ParamCapability> getParamCapabilities(@NonNull List<String> names) {
try {
- return mService.getParamCapabilities(names, mUserId);
+ return mService.getParamCapabilities(names, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -516,7 +570,7 @@
@NonNull
public List<String> getPictureProfileAllowList() {
try {
- return mService.getPictureProfileAllowList(mUserId);
+ return mService.getPictureProfileAllowList(mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -530,7 +584,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public void setPictureProfileAllowList(@NonNull List<String> packageNames) {
try {
- mService.setPictureProfileAllowList(packageNames, mUserId);
+ mService.setPictureProfileAllowList(packageNames, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -548,7 +602,7 @@
@NonNull
public List<String> getSoundProfileAllowList() {
try {
- return mService.getSoundProfileAllowList(mUserId);
+ return mService.getSoundProfileAllowList(mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -562,7 +616,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
public void setSoundProfileAllowList(@NonNull List<String> packageNames) {
try {
- mService.setSoundProfileAllowList(packageNames, mUserId);
+ mService.setSoundProfileAllowList(packageNames, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -574,7 +628,7 @@
*/
public boolean isSupported() {
try {
- return mService.isSupported(mUserId);
+ return mService.isSupported(mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -592,7 +646,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public void setAutoPictureQualityEnabled(boolean enabled) {
try {
- mService.setAutoPictureQualityEnabled(enabled, mUserId);
+ mService.setAutoPictureQualityEnabled(enabled, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -603,7 +657,7 @@
*/
public boolean isAutoPictureQualityEnabled() {
try {
- return mService.isAutoPictureQualityEnabled(mUserId);
+ return mService.isAutoPictureQualityEnabled(mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -620,7 +674,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public void setSuperResolutionEnabled(boolean enabled) {
try {
- mService.setSuperResolutionEnabled(enabled, mUserId);
+ mService.setSuperResolutionEnabled(enabled, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -631,7 +685,7 @@
*/
public boolean isSuperResolutionEnabled() {
try {
- return mService.isSuperResolutionEnabled(mUserId);
+ return mService.isSuperResolutionEnabled(mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -649,7 +703,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
public void setAutoSoundQualityEnabled(boolean enabled) {
try {
- mService.setAutoSoundQualityEnabled(enabled, mUserId);
+ mService.setAutoSoundQualityEnabled(enabled, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -660,7 +714,7 @@
*/
public boolean isAutoSoundQualityEnabled() {
try {
- return mService.isAutoSoundQualityEnabled(mUserId);
+ return mService.isAutoSoundQualityEnabled(mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -709,7 +763,7 @@
@NonNull AmbientBacklightSettings settings) {
Preconditions.checkNotNull(settings);
try {
- mService.setAmbientBacklightSettings(settings, mUserId);
+ mService.setAmbientBacklightSettings(settings, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -720,7 +774,7 @@
*/
public boolean isAmbientBacklightEnabled() {
try {
- return mService.isAmbientBacklightEnabled(mUserId);
+ return mService.isAmbientBacklightEnabled(mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -734,7 +788,7 @@
@RequiresPermission(android.Manifest.permission.READ_COLOR_ZONES)
public void setAmbientBacklightEnabled(boolean enabled) {
try {
- mService.setAmbientBacklightEnabled(enabled, mUserId);
+ mService.setAmbientBacklightEnabled(enabled, mUserHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -791,11 +845,11 @@
});
}
- public void postError(int error) {
+ public void postError(String profileId, int error) {
mExecutor.execute(new Runnable() {
@Override
public void run() {
- mCallback.onError(error);
+ mCallback.onError(profileId, error);
}
});
}
@@ -851,11 +905,11 @@
});
}
- public void postError(int error) {
+ public void postError(String profileId, int error) {
mExecutor.execute(new Runnable() {
@Override
public void run() {
- mCallback.onError(error);
+ mCallback.onError(profileId, error);
}
});
}
@@ -921,9 +975,11 @@
/**
* This is invoked when an issue has occurred.
*
+ * @param profileId the profile ID related to the error. {@code null} if there is no
+ * associated profile.
* @param errorCode the error code
*/
- public void onError(@PictureProfile.ErrorCode int errorCode) {
+ public void onError(@Nullable String profileId, @PictureProfile.ErrorCode int errorCode) {
}
/**
@@ -976,9 +1032,11 @@
/**
* This is invoked when an issue has occurred.
*
+ * @param profileId the profile ID related to the error. {@code null} if there is no
+ * associated profile.
* @param errorCode the error code
*/
- public void onError(@SoundProfile.ErrorCode int errorCode) {
+ public void onError(@Nullable String profileId, @SoundProfile.ErrorCode int errorCode) {
}
/**
@@ -1004,4 +1062,86 @@
public void onAmbientBacklightEvent(@NonNull AmbientBacklightEvent event) {
}
}
+
+ /**
+ * Listener used to monitor status of active pictures.
+ */
+ public interface ActiveProcessingPictureListener {
+ /**
+ * Called when active pictures are changed.
+ *
+ * @param activeProcessingPictures contents currently undergoing picture processing.
+ */
+ void onActiveProcessingPicturesChanged(
+ @NonNull List<ActiveProcessingPicture> activeProcessingPictures);
+ }
+
+ /**
+ * Adds an active picture listener for the contents owner by the caller.
+ */
+ public void addActiveProcessingPictureListener(
+ @CallbackExecutor @NonNull Executor executor,
+ @NonNull ActiveProcessingPictureListener listener) {
+ Preconditions.checkNotNull(listener);
+ Preconditions.checkNotNull(executor);
+ synchronized (mLock) {
+ mApListenerRecords.add(
+ new ActiveProcessingPictureListenerRecord(listener, executor, false));
+ }
+ }
+
+ /**
+ * Adds an active picture listener for all contents.
+ *
+ * @hide
+ */
+ @SuppressLint("PairedRegistration")
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
+ public void addGlobalActiveProcessingPictureListener(
+ @NonNull Executor executor,
+ @NonNull ActiveProcessingPictureListener listener) {
+ Preconditions.checkNotNull(listener);
+ Preconditions.checkNotNull(executor);
+ synchronized (mLock) {
+ mApListenerRecords.add(
+ new ActiveProcessingPictureListenerRecord(listener, executor, true));
+ }
+ }
+
+
+ /**
+ * Removes an active picture listener for the contents.
+ */
+ public void removeActiveProcessingPictureListener(
+ @NonNull ActiveProcessingPictureListener listener) {
+ Preconditions.checkNotNull(listener);
+ synchronized (mLock) {
+ for (Iterator<ActiveProcessingPictureListenerRecord> it = mApListenerRecords.iterator();
+ it.hasNext(); ) {
+ ActiveProcessingPictureListenerRecord record = it.next();
+ if (record.getListener() == listener) {
+ it.remove();
+ break;
+ }
+ }
+ }
+ }
+
+ private static final class ActiveProcessingPictureListenerRecord {
+ private final ActiveProcessingPictureListener mListener;
+ private final Executor mExecutor;
+ private final boolean mIsGlobal;
+
+ ActiveProcessingPictureListenerRecord(
+ ActiveProcessingPictureListener listener, Executor executor, boolean isGlobal) {
+ mListener = listener;
+ mExecutor = executor;
+ mIsGlobal = isGlobal;
+ }
+
+ public ActiveProcessingPictureListener getListener() {
+ return mListener;
+ }
+ }
}
diff --git a/media/java/android/media/quality/PictureProfile.java b/media/java/android/media/quality/PictureProfile.java
index dcb4222..6064485 100644
--- a/media/java/android/media/quality/PictureProfile.java
+++ b/media/java/android/media/quality/PictureProfile.java
@@ -48,6 +48,7 @@
private final String mPackageName;
@NonNull
private final PersistableBundle mParams;
+ private final PictureProfileHandle mHandle;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -121,6 +122,7 @@
mInputId = in.readString();
mPackageName = in.readString();
mParams = in.readPersistableBundle();
+ mHandle = in.readParcelable(PictureProfileHandle.class.getClassLoader());
}
@Override
@@ -131,6 +133,7 @@
dest.writeString(mInputId);
dest.writeString(mPackageName);
dest.writePersistableBundle(mParams);
+ dest.writeParcelable(mHandle, flags);
}
@Override
@@ -163,13 +166,15 @@
@NonNull String name,
@Nullable String inputId,
@NonNull String packageName,
- @NonNull PersistableBundle params) {
+ @NonNull PersistableBundle params,
+ @NonNull PictureProfileHandle handle) {
this.mId = id;
this.mType = type;
this.mName = name;
this.mInputId = inputId;
this.mPackageName = packageName;
this.mParams = params;
+ this.mHandle = handle;
}
/**
@@ -251,6 +256,15 @@
}
/**
+ * Gets profile handle
+ * @hide
+ */
+ @NonNull
+ public PictureProfileHandle getHandle() {
+ return mHandle;
+ }
+
+ /**
* A builder for {@link PictureProfile}.
*/
public static final class Builder {
@@ -265,6 +279,7 @@
private String mPackageName;
@NonNull
private PersistableBundle mParams;
+ private PictureProfileHandle mHandle;
/**
* Creates a new Builder.
@@ -283,6 +298,7 @@
mPackageName = p.getPackageName();
mInputId = p.getInputId();
mParams = p.getParameters();
+ mHandle = p.getHandle();
}
/**
@@ -350,6 +366,16 @@
}
/**
+ * Sets profile handle.
+ * @hide
+ */
+ @NonNull
+ public Builder setHandle(@NonNull PictureProfileHandle handle) {
+ mHandle = handle;
+ return this;
+ }
+
+ /**
* Builds the instance.
*/
@NonNull
@@ -361,7 +387,8 @@
mName,
mInputId,
mPackageName,
- mParams);
+ mParams,
+ mHandle);
return o;
}
}
diff --git a/media/java/android/media/quality/PictureProfileHandle.java b/media/java/android/media/quality/PictureProfileHandle.java
index 714fd36..d9d2193 100644
--- a/media/java/android/media/quality/PictureProfileHandle.java
+++ b/media/java/android/media/quality/PictureProfileHandle.java
@@ -28,11 +28,14 @@
* A picture profile represents a collection of parameters used to configure picture processing
* to enhance the quality of graphic buffers.
*
+ * @see PictureProfile.getHandle
+ *
* @hide
*/
@SystemApi
@FlaggedApi(android.media.tv.flags.Flags.FLAG_APPLY_PICTURE_PROFILES)
public final class PictureProfileHandle implements Parcelable {
+ /** A handle that represents no picture processing configuration. */
public static final @NonNull PictureProfileHandle NONE = new PictureProfileHandle(0);
private final long mId;
@@ -42,7 +45,16 @@
mId = id;
}
- /** @hide */
+ /**
+ * An ID that uniquely identifies the picture profile across the system.
+ *
+ * This ID can be used to construct an NDK PictureProfileHandle to be fed directly into
+ * IGraphicBufferProducer to couple a picture profile to a graphic buffer.
+ *
+ * Note: These IDs are generated randomly and are not stable across reboots.
+ *
+ * @hide
+ */
@SystemApi
@FlaggedApi(android.media.tv.flags.Flags.FLAG_APPLY_PICTURE_PROFILES)
public long getId() {
diff --git a/media/java/android/media/quality/SoundProfile.java b/media/java/android/media/quality/SoundProfile.java
index c7fb4dd..1dd59ab 100644
--- a/media/java/android/media/quality/SoundProfile.java
+++ b/media/java/android/media/quality/SoundProfile.java
@@ -48,6 +48,7 @@
private final String mPackageName;
@NonNull
private final PersistableBundle mParams;
+ private final SoundProfileHandle mHandle;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -120,6 +121,7 @@
mInputId = in.readString();
mPackageName = in.readString();
mParams = in.readPersistableBundle();
+ mHandle = in.readParcelable(SoundProfileHandle.class.getClassLoader());
}
@Override
@@ -130,6 +132,7 @@
dest.writeString(mInputId);
dest.writeString(mPackageName);
dest.writePersistableBundle(mParams);
+ dest.writeParcelable(mHandle, flags);
}
@Override
@@ -162,13 +165,15 @@
@NonNull String name,
@Nullable String inputId,
@NonNull String packageName,
- @NonNull PersistableBundle params) {
+ @NonNull PersistableBundle params,
+ @NonNull SoundProfileHandle handle) {
this.mId = id;
this.mType = type;
this.mName = name;
this.mInputId = inputId;
this.mPackageName = packageName;
this.mParams = params;
+ this.mHandle = handle;
}
/**
@@ -250,6 +255,15 @@
}
/**
+ * Gets profile handle
+ * @hide
+ */
+ @NonNull
+ public SoundProfileHandle getHandle() {
+ return mHandle;
+ }
+
+ /**
* A builder for {@link SoundProfile}
*/
public static final class Builder {
@@ -264,6 +278,7 @@
private String mPackageName;
@NonNull
private PersistableBundle mParams;
+ private SoundProfileHandle mHandle;
/**
* Creates a new Builder.
@@ -282,6 +297,7 @@
mPackageName = p.getPackageName();
mInputId = p.getInputId();
mParams = p.getParameters();
+ mHandle = p.getHandle();
}
/**
@@ -349,6 +365,16 @@
}
/**
+ * Sets profile handle.
+ * @hide
+ */
+ @NonNull
+ public Builder setHandle(@NonNull SoundProfileHandle handle) {
+ mHandle = handle;
+ return this;
+ }
+
+ /**
* Builds the instance.
*/
@NonNull
@@ -360,7 +386,8 @@
mName,
mInputId,
mPackageName,
- mParams);
+ mParams,
+ mHandle);
return o;
}
}
diff --git a/core/java/android/app/ondeviceintelligence/Feature.aidl b/media/java/android/media/quality/SoundProfileHandle.aidl
similarity index 80%
copy from core/java/android/app/ondeviceintelligence/Feature.aidl
copy to media/java/android/media/quality/SoundProfileHandle.aidl
index 18494d7..6b8161c 100644
--- a/core/java/android/app/ondeviceintelligence/Feature.aidl
+++ b/media/java/android/media/quality/SoundProfileHandle.aidl
@@ -5,7 +5,7 @@
* you may not use this 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,
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-package android.app.ondeviceintelligence;
+package android.media.quality;
-/**
- * @hide
- */
-parcelable Feature;
+parcelable SoundProfileHandle;
diff --git a/media/java/android/media/quality/SoundProfileHandle.java b/media/java/android/media/quality/SoundProfileHandle.java
new file mode 100644
index 0000000..edb546e
--- /dev/null
+++ b/media/java/android/media/quality/SoundProfileHandle.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.quality;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A type-safe handle to a sound profile.
+ *
+ * @hide
+ */
+public final class SoundProfileHandle implements Parcelable {
+ public static final @NonNull SoundProfileHandle NONE = new SoundProfileHandle(-1000);
+
+ private final long mId;
+
+ /** @hide */
+ public SoundProfileHandle(long id) {
+ mId = id;
+ }
+
+ /** @hide */
+ public long getId() {
+ return mId;
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mId);
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ public static final @NonNull Creator<SoundProfileHandle> CREATOR =
+ new Creator<SoundProfileHandle>() {
+ @Override
+ public SoundProfileHandle createFromParcel(Parcel in) {
+ return new SoundProfileHandle(in);
+ }
+
+ @Override
+ public SoundProfileHandle[] newArray(int size) {
+ return new SoundProfileHandle[size];
+ }
+ };
+
+ private SoundProfileHandle(@NonNull Parcel in) {
+ mId = in.readLong();
+ }
+}
diff --git a/native/android/Android.bp b/native/android/Android.bp
index cd6de5a..129d616 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -73,6 +73,7 @@
"surface_control.cpp",
"surface_texture.cpp",
"system_fonts.cpp",
+ "system_health.cpp",
"trace.cpp",
"thermal.cpp",
],
diff --git a/native/android/dynamic_instrumentation_manager.cpp b/native/android/dynamic_instrumentation_manager.cpp
index 5322136..0749731 100644
--- a/native/android/dynamic_instrumentation_manager.cpp
+++ b/native/android/dynamic_instrumentation_manager.cpp
@@ -15,7 +15,9 @@
*/
#define LOG_TAG "ADynamicInstrumentationManager"
+#include <android-base/properties.h>
#include <android/dynamic_instrumentation_manager.h>
+#include <android/os/instrumentation/BnOffsetCallback.h>
#include <android/os/instrumentation/ExecutableMethodFileOffsets.h>
#include <android/os/instrumentation/IDynamicInstrumentationManager.h>
#include <android/os/instrumentation/MethodDescriptor.h>
@@ -23,7 +25,9 @@
#include <binder/Binder.h>
#include <binder/IServiceManager.h>
#include <utils/Log.h>
+#include <utils/StrongPointer.h>
+#include <future>
#include <mutex>
#include <optional>
#include <string>
@@ -31,6 +35,9 @@
namespace android::dynamicinstrumentationmanager {
+using android::os::instrumentation::BnOffsetCallback;
+using android::os::instrumentation::ExecutableMethodFileOffsets;
+
// Global instance of IDynamicInstrumentationManager, service is obtained only on first use.
static std::mutex mLock;
static sp<os::instrumentation::IDynamicInstrumentationManager> mService;
@@ -131,6 +138,30 @@
delete instance;
}
+class ResultCallback : public BnOffsetCallback {
+public:
+ ::android::binder::Status onResult(
+ const ::std::optional<ExecutableMethodFileOffsets>& offsets) override {
+ promise_.set_value(offsets);
+ return android::binder::Status::ok();
+ }
+
+ std::optional<ExecutableMethodFileOffsets> waitForResult() {
+ std::future<std::optional<ExecutableMethodFileOffsets>> futureResult =
+ promise_.get_future();
+ auto futureStatus = futureResult.wait_for(
+ std::chrono::seconds(1 * android::base::HwTimeoutMultiplier()));
+ if (futureStatus == std::future_status::ready) {
+ return futureResult.get();
+ } else {
+ return std::nullopt;
+ }
+ }
+
+private:
+ std::promise<std::optional<ExecutableMethodFileOffsets>> promise_;
+};
+
int32_t ADynamicInstrumentationManager_getExecutableMethodFileOffsets(
const ADynamicInstrumentationManager_TargetProcess* targetProcess,
const ADynamicInstrumentationManager_MethodDescriptor* methodDescriptor,
@@ -150,15 +181,15 @@
return INVALID_OPERATION;
}
- std::optional<android::os::instrumentation::ExecutableMethodFileOffsets> offsets;
+ android::sp<ResultCallback> resultCallback = android::sp<ResultCallback>::make();
binder_status_t result =
service->getExecutableMethodFileOffsets(targetProcessParcel, methodDescriptorParcel,
- &offsets)
+ resultCallback)
.exceptionCode();
if (result != OK) {
return result;
}
-
+ std::optional<ExecutableMethodFileOffsets> offsets = resultCallback->waitForResult();
if (offsets != std::nullopt) {
auto* value = new ADynamicInstrumentationManager_ExecutableMethodFileOffsets();
value->containerPath = offsets->containerPath;
@@ -170,4 +201,4 @@
}
return result;
-}
\ No newline at end of file
+}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 78a7ddb..1ccadf9 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -320,6 +320,23 @@
ASystemFontIterator_open; # introduced=29
ASystemFontIterator_close; # introduced=29
ASystemFontIterator_next; # introduced=29
+ ASystemHealth_getCpuHeadroom; # introduced=36
+ ASystemHealth_getGpuHeadroom; # introduced=36
+ ASystemHealth_getCpuHeadroomMinIntervalMillis; # introduced=36
+ ASystemHealth_getGpuHeadroomMinIntervalMillis; # introduced=36
+ ACpuHeadroomParams_create; # introduced=36
+ ACpuHeadroomParams_destroy; # introduced=36
+ ACpuHeadroomParams_setCalculationType; # introduced=36
+ ACpuHeadroomParams_getCalculationType; # introduced=36
+ ACpuHeadroomParams_setCalculationWindowMillis; # introduced=36
+ ACpuHeadroomParams_getCalculationWindowMillis; # introduced=36
+ ACpuHeadroomParams_setTids; # introduced=36
+ AGpuHeadroomParams_create; # introduced=36
+ AGpuHeadroomParams_destroy; # introduced=36
+ AGpuHeadroomParams_setCalculationType; # introduced=36
+ AGpuHeadroomParams_getCalculationType; # introduced=36
+ AGpuHeadroomParams_setCalculationWindowMillis; # introduced=36
+ AGpuHeadroomParams_getCalculationWindowMillis; # introduced=36
AFont_close; # introduced=29
AFont_getFontFilePath; # introduced=29
AFont_getWeight; # introduced=29
@@ -376,6 +393,7 @@
APerformanceHint_createSessionUsingConfig; # introduced=36
APerformanceHint_notifyWorkloadIncrease; # introduced=36
APerformanceHint_notifyWorkloadReset; # introduced=36
+ APerformanceHint_notifyWorkloadSpike; # introduced=36
APerformanceHint_borrowSessionFromJava; # introduced=36
APerformanceHint_setNativeSurfaces; # introduced=36
AWorkDuration_create; # introduced=VanillaIceCream
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 608c01c..1945d90 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -214,6 +214,7 @@
int sendHints(std::vector<hal::SessionHint>& hints, int64_t now, const char* debugName);
int notifyWorkloadIncrease(bool cpu, bool gpu, const char* debugName);
int notifyWorkloadReset(bool cpu, bool gpu, const char* debugName);
+ int notifyWorkloadSpike(bool cpu, bool gpu, const char* debugName);
int setThreads(const int32_t* threadIds, size_t size);
int getThreadIds(int32_t* const threadIds, size_t* size);
int setPreferPowerEfficiency(bool enabled);
@@ -328,7 +329,7 @@
bool APerformanceHintManager::canSendLoadHints(std::vector<hal::SessionHint>& hints, int64_t now) {
mHintBudget =
- std::max(kMaxLoadHintsPerInterval,
+ std::min(kMaxLoadHintsPerInterval,
mHintBudget +
static_cast<double>(now - mLastBudgetReplenish) * kReplenishRate);
mLastBudgetReplenish = now;
@@ -600,6 +601,19 @@
return sendHints(hints, now, debugName);
}
+int APerformanceHintSession::notifyWorkloadSpike(bool cpu, bool gpu, const char* debugName) {
+ std::vector<hal::SessionHint> hints(2);
+ hints.clear();
+ if (cpu) {
+ hints.push_back(hal::SessionHint::CPU_LOAD_SPIKE);
+ }
+ if (gpu) {
+ hints.push_back(hal::SessionHint::GPU_LOAD_SPIKE);
+ }
+ int64_t now = ::android::uptimeNanos();
+ return sendHints(hints, now, debugName);
+}
+
int APerformanceHintSession::setThreads(const int32_t* threadIds, size_t size) {
if (size == 0) {
ALOGE("%s: the list of thread ids must not be empty.", __FUNCTION__);
@@ -1149,6 +1163,16 @@
return session->notifyWorkloadReset(cpu, gpu, debugName);
}
+int APerformanceHint_notifyWorkloadSpike(APerformanceHintSession* session, bool cpu, bool gpu,
+ const char* debugName) {
+ VALIDATE_PTR(session)
+ VALIDATE_PTR(debugName)
+ if (!useNewLoadHintBehavior()) {
+ return ENOTSUP;
+ }
+ return session->notifyWorkloadReset(cpu, gpu, debugName);
+}
+
int APerformanceHint_setNativeSurfaces(APerformanceHintSession* session,
ANativeWindow** nativeWindows, int nativeWindowsSize,
ASurfaceControl** surfaceControls, int surfaceControlsSize) {
diff --git a/native/android/system_health.cpp b/native/android/system_health.cpp
new file mode 100644
index 0000000..f3fa9f6
--- /dev/null
+++ b/native/android/system_health.cpp
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <aidl/android/hardware/power/CpuHeadroomParams.h>
+#include <aidl/android/hardware/power/GpuHeadroomParams.h>
+#include <aidl/android/os/CpuHeadroomParamsInternal.h>
+#include <aidl/android/os/GpuHeadroomParamsInternal.h>
+#include <aidl/android/os/IHintManager.h>
+#include <android/binder_manager.h>
+#include <android/system_health.h>
+#include <binder/IServiceManager.h>
+#include <binder/Status.h>
+
+using namespace android;
+using namespace aidl::android::os;
+namespace hal = aidl::android::hardware::power;
+
+struct ACpuHeadroomParams : public CpuHeadroomParamsInternal {};
+struct AGpuHeadroomParams : public GpuHeadroomParamsInternal {};
+
+const int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50;
+const int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000;
+const int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50;
+const int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000;
+const int CPU_HEADROOM_MAX_TID_COUNT = 5;
+
+struct ASystemHealthManager {
+public:
+ static ASystemHealthManager* getInstance();
+ ASystemHealthManager(std::shared_ptr<IHintManager>& hintManager);
+ ASystemHealthManager() = delete;
+ ~ASystemHealthManager();
+ int getCpuHeadroom(const ACpuHeadroomParams* params, float* outHeadroom);
+ int getGpuHeadroom(const AGpuHeadroomParams* params, float* outHeadroom);
+ int getCpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis);
+ int getGpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis);
+
+private:
+ static ASystemHealthManager* create(std::shared_ptr<IHintManager> hintManager);
+ std::shared_ptr<IHintManager> mHintManager;
+};
+
+ASystemHealthManager* ASystemHealthManager::getInstance() {
+ static std::once_flag creationFlag;
+ static ASystemHealthManager* instance = nullptr;
+ std::call_once(creationFlag, []() { instance = create(nullptr); });
+ return instance;
+}
+
+ASystemHealthManager::ASystemHealthManager(std::shared_ptr<IHintManager>& hintManager)
+ : mHintManager(std::move(hintManager)) {}
+
+ASystemHealthManager::~ASystemHealthManager() {}
+
+ASystemHealthManager* ASystemHealthManager::create(std::shared_ptr<IHintManager> hintManager) {
+ if (!hintManager) {
+ hintManager = IHintManager::fromBinder(
+ ndk::SpAIBinder(AServiceManager_waitForService("performance_hint")));
+ }
+ if (hintManager == nullptr) {
+ ALOGE("%s: PerformanceHint service is not ready ", __FUNCTION__);
+ return nullptr;
+ }
+ return new ASystemHealthManager(hintManager);
+}
+
+ASystemHealthManager* ASystemHealth_acquireManager() {
+ return ASystemHealthManager::getInstance();
+}
+
+int ASystemHealthManager::getCpuHeadroom(const ACpuHeadroomParams* params, float* outHeadroom) {
+ std::optional<hal::CpuHeadroomResult> res;
+ ::ndk::ScopedAStatus ret;
+ CpuHeadroomParamsInternal internalParams;
+ if (!params) {
+ ret = mHintManager->getCpuHeadroom(internalParams, &res);
+ } else {
+ ret = mHintManager->getCpuHeadroom(*params, &res);
+ }
+ if (!ret.isOk()) {
+ LOG_ALWAYS_FATAL_IF(ret.getExceptionCode() == EX_ILLEGAL_ARGUMENT,
+ "Invalid ACpuHeadroomParams: %s", ret.getMessage());
+ ALOGE("ASystemHealth_getCpuHeadroom fails: %s", ret.getMessage());
+ if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+ return ENOTSUP;
+ } else if (ret.getExceptionCode() == EX_SECURITY) {
+ return EPERM;
+ }
+ return EPIPE;
+ }
+ *outHeadroom = res->get<hal::CpuHeadroomResult::Tag::globalHeadroom>();
+ return OK;
+}
+
+int ASystemHealthManager::getGpuHeadroom(const AGpuHeadroomParams* params, float* outHeadroom) {
+ std::optional<hal::GpuHeadroomResult> res;
+ ::ndk::ScopedAStatus ret;
+ GpuHeadroomParamsInternal internalParams;
+ if (!params) {
+ ret = mHintManager->getGpuHeadroom(internalParams, &res);
+ } else {
+ ret = mHintManager->getGpuHeadroom(*params, &res);
+ }
+ if (!ret.isOk()) {
+ LOG_ALWAYS_FATAL_IF(ret.getExceptionCode() == EX_ILLEGAL_ARGUMENT,
+ "Invalid AGpuHeadroomParams: %s", ret.getMessage());
+ ALOGE("ASystemHealth_getGpuHeadroom fails: %s", ret.getMessage());
+ if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+ return ENOTSUP;
+ }
+ return EPIPE;
+ }
+ *outHeadroom = res->get<hal::GpuHeadroomResult::Tag::globalHeadroom>();
+ return OK;
+}
+
+int ASystemHealthManager::getCpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis) {
+ int64_t minIntervalMillis = 0;
+ ::ndk::ScopedAStatus ret = mHintManager->getCpuHeadroomMinIntervalMillis(&minIntervalMillis);
+ if (!ret.isOk()) {
+ ALOGE("ASystemHealth_getCpuHeadroomMinIntervalMillis fails: %s", ret.getMessage());
+ if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+ return ENOTSUP;
+ }
+ return EPIPE;
+ }
+ *outMinIntervalMillis = minIntervalMillis;
+ return OK;
+}
+
+int ASystemHealthManager::getGpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis) {
+ int64_t minIntervalMillis = 0;
+ ::ndk::ScopedAStatus ret = mHintManager->getGpuHeadroomMinIntervalMillis(&minIntervalMillis);
+ if (!ret.isOk()) {
+ ALOGE("ASystemHealth_getGpuHeadroomMinIntervalMillis fails: %s", ret.getMessage());
+ if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+ return ENOTSUP;
+ }
+ return EPIPE;
+ }
+ *outMinIntervalMillis = minIntervalMillis;
+ return OK;
+}
+
+int ASystemHealth_getCpuHeadroom(const ACpuHeadroomParams* _Nullable params,
+ float* _Nonnull outHeadroom) {
+ LOG_ALWAYS_FATAL_IF(outHeadroom == nullptr, "%s: outHeadroom should not be null", __FUNCTION__);
+ auto manager = ASystemHealthManager::getInstance();
+ if (manager == nullptr) return ENOTSUP;
+ return manager->getCpuHeadroom(params, outHeadroom);
+}
+
+int ASystemHealth_getGpuHeadroom(const AGpuHeadroomParams* _Nullable params,
+ float* _Nonnull outHeadroom) {
+ LOG_ALWAYS_FATAL_IF(outHeadroom == nullptr, "%s: outHeadroom should not be null", __FUNCTION__);
+ auto manager = ASystemHealthManager::getInstance();
+ if (manager == nullptr) return ENOTSUP;
+ return manager->getGpuHeadroom(params, outHeadroom);
+}
+
+int ASystemHealth_getCpuHeadroomMinIntervalMillis(int64_t* _Nonnull outMinIntervalMillis) {
+ LOG_ALWAYS_FATAL_IF(outMinIntervalMillis == nullptr,
+ "%s: outMinIntervalMillis should not be null", __FUNCTION__);
+ auto manager = ASystemHealthManager::getInstance();
+ if (manager == nullptr) return ENOTSUP;
+ return manager->getCpuHeadroomMinIntervalMillis(outMinIntervalMillis);
+}
+
+int ASystemHealth_getGpuHeadroomMinIntervalMillis(int64_t* _Nonnull outMinIntervalMillis) {
+ LOG_ALWAYS_FATAL_IF(outMinIntervalMillis == nullptr,
+ "%s: outMinIntervalMillis should not be null", __FUNCTION__);
+ auto manager = ASystemHealthManager::getInstance();
+ if (manager == nullptr) return ENOTSUP;
+ return manager->getGpuHeadroomMinIntervalMillis(outMinIntervalMillis);
+}
+
+void ACpuHeadroomParams_setCalculationWindowMillis(ACpuHeadroomParams* _Nonnull params,
+ int windowMillis) {
+ LOG_ALWAYS_FATAL_IF(windowMillis < CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN ||
+ windowMillis > CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX,
+ "%s: windowMillis should be in range [50, 10000] but got %d", __FUNCTION__,
+ windowMillis);
+ params->calculationWindowMillis = windowMillis;
+}
+
+void AGpuHeadroomParams_setCalculationWindowMillis(AGpuHeadroomParams* _Nonnull params,
+ int windowMillis) {
+ LOG_ALWAYS_FATAL_IF(windowMillis < GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN ||
+ windowMillis > GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX,
+ "%s: windowMillis should be in range [50, 10000] but got %d", __FUNCTION__,
+ windowMillis);
+ params->calculationWindowMillis = windowMillis;
+}
+
+int ACpuHeadroomParams_getCalculationWindowMillis(ACpuHeadroomParams* _Nonnull params) {
+ return params->calculationWindowMillis;
+}
+
+int AGpuHeadroomParams_getCalculationWindowMillis(AGpuHeadroomParams* _Nonnull params) {
+ return params->calculationWindowMillis;
+}
+
+void ACpuHeadroomParams_setTids(ACpuHeadroomParams* _Nonnull params, const int* _Nonnull tids,
+ int tidsSize) {
+ LOG_ALWAYS_FATAL_IF(tids == nullptr, "%s: tids should not be null", __FUNCTION__);
+ LOG_ALWAYS_FATAL_IF(tidsSize > CPU_HEADROOM_MAX_TID_COUNT, "%s: tids size should not exceed 5",
+ __FUNCTION__);
+ params->tids.resize(tidsSize);
+ params->tids.clear();
+ for (int i = 0; i < tidsSize; ++i) {
+ LOG_ALWAYS_FATAL_IF(tids[i] <= 0, "ACpuHeadroomParams_setTids: Invalid non-positive tid %d",
+ tids[i]);
+ params->tids[i] = tids[i];
+ }
+}
+
+void ACpuHeadroomParams_setCalculationType(ACpuHeadroomParams* _Nonnull params,
+ ACpuHeadroomCalculationType calculationType) {
+ LOG_ALWAYS_FATAL_IF(calculationType < ACpuHeadroomCalculationType::
+ ACPU_HEADROOM_CALCULATION_TYPE_MIN ||
+ calculationType > ACpuHeadroomCalculationType::
+ ACPU_HEADROOM_CALCULATION_TYPE_AVERAGE,
+ "%s: calculationType should be one of ACpuHeadroomCalculationType values "
+ "but got %d",
+ __FUNCTION__, calculationType);
+ params->calculationType = static_cast<hal::CpuHeadroomParams::CalculationType>(calculationType);
+}
+
+ACpuHeadroomCalculationType ACpuHeadroomParams_getCalculationType(
+ ACpuHeadroomParams* _Nonnull params) {
+ return static_cast<ACpuHeadroomCalculationType>(params->calculationType);
+}
+
+void AGpuHeadroomParams_setCalculationType(AGpuHeadroomParams* _Nonnull params,
+ AGpuHeadroomCalculationType calculationType) {
+ LOG_ALWAYS_FATAL_IF(calculationType < AGpuHeadroomCalculationType::
+ AGPU_HEADROOM_CALCULATION_TYPE_MIN ||
+ calculationType > AGpuHeadroomCalculationType::
+ AGPU_HEADROOM_CALCULATION_TYPE_AVERAGE,
+ "%s: calculationType should be one of AGpuHeadroomCalculationType values "
+ "but got %d",
+ __FUNCTION__, calculationType);
+ params->calculationType = static_cast<hal::GpuHeadroomParams::CalculationType>(calculationType);
+}
+
+AGpuHeadroomCalculationType AGpuHeadroomParams_getCalculationType(
+ AGpuHeadroomParams* _Nonnull params) {
+ return static_cast<AGpuHeadroomCalculationType>(params->calculationType);
+}
+
+ACpuHeadroomParams* _Nonnull ACpuHeadroomParams_create() {
+ return new ACpuHeadroomParams();
+}
+
+AGpuHeadroomParams* _Nonnull AGpuHeadroomParams_create() {
+ return new AGpuHeadroomParams();
+}
+
+void ACpuHeadroomParams_destroy(ACpuHeadroomParams* _Nonnull params) {
+ delete params;
+}
+
+void AGpuHeadroomParams_destroy(AGpuHeadroomParams* _Nonnull params) {
+ delete params;
+}
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index b8f574f..c166e73 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -299,6 +299,10 @@
EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::GPU_LOAD_RESET))).Times(Exactly(1));
result = APerformanceHint_notifyWorkloadReset(session, true, true, "Test hint");
EXPECT_EQ(0, result);
+ EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::CPU_LOAD_SPIKE))).Times(Exactly(1));
+ EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::GPU_LOAD_SPIKE))).Times(Exactly(1));
+ result = APerformanceHint_notifyWorkloadSpike(session, true, true, "Test hint");
+ EXPECT_EQ(0, result);
result = APerformanceHint_sendHint(session, static_cast<SessionHint>(-1));
EXPECT_EQ(EINVAL, result);
diff --git a/nfc/Android.bp b/nfc/Android.bp
index 7ad8c4c..abe0ab7 100644
--- a/nfc/Android.bp
+++ b/nfc/Android.bp
@@ -37,6 +37,7 @@
java_sdk_library {
name: "framework-nfc",
libs: [
+ "androidx.annotation_annotation",
"unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
"framework-permission-s.stubs.module_lib",
"framework-permission.stubs.module_lib",
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index e97b15d..cf11ac0 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -85,6 +85,10 @@
field public static final int HCE_ACTIVATE = 1; // 0x1
field public static final int HCE_DATA_TRANSFERRED = 2; // 0x2
field public static final int HCE_DEACTIVATE = 3; // 0x3
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int NFCEE_TECH_A = 1; // 0x1
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int NFCEE_TECH_B = 2; // 0x2
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int NFCEE_TECH_F = 4; // 0x4
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int NFCEE_TECH_NONE = 0; // 0x0
field public static final int POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE = 2; // 0x2
field public static final int POLLING_STATE_CHANGE_SUCCEEDED = 1; // 0x1
field public static final int STATUS_OK = 0; // 0x0
@@ -250,6 +254,7 @@
field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_INVALID_SUBSCRIPTION_ID = 1; // 0x1
field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_NOT_SUPPORTED = 3; // 0x3
field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_SUCCESS = 0; // 0x0
+ field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_UNKNOWN = -1; // 0xffffffff
}
}
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index fb11875..b46e343 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -150,28 +150,24 @@
/**
* Technology Type for {@link #getActiveNfceeList()}.
- * @hide
*/
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
public static final int NFCEE_TECH_NONE = 0;
/**
* Technology Type for {@link #getActiveNfceeList()}.
- * @hide
*/
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
public static final int NFCEE_TECH_A = 1;
/**
* Technology Type for {@link #getActiveNfceeList()}.
- * @hide
*/
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
public static final int NFCEE_TECH_B = 1 << 1;
/**
* Technology Type for {@link #getActiveNfceeList()}.
- * @hide
*/
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
public static final int NFCEE_TECH_F = 1 << 2;
@@ -670,12 +666,15 @@
/**
* Get the Active NFCEE (NFC Execution Environment) List
*
- * @see Reader#getName() for the list of possible NFCEE names.
- *
* @return Map< String, @NfceeTechnology Integer >
* A HashMap where keys are activated secure elements and
- * the values are bitmap of technologies supported by each secure element
- * on success keys can contain "eSE" and "UICC", otherwise empty map.
+ * the values are bitmap of technologies supported by each secure element:
+ * NFCEE_TECH_A == 0x1
+ * NFCEE_TECH_B == 0x2
+ * NFCEE_TECH_F == 0x4
+ * and keys can contain "eSE" and "SIM" with a number,
+ * in case of failure an empty map is returned.
+ * @see Reader#getName() for the list of possible NFCEE names.
*/
@NonNull
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java
index 8037702..e0bc15f 100644
--- a/nfc/java/android/nfc/cardemulation/CardEmulation.java
+++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java
@@ -1114,6 +1114,14 @@
@FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC)
public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_NOT_SUPPORTED = 3;
+ /**
+ * Setting the default subscription ID failed because of unknown error.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_CARD_EMULATION_EUICC)
+ public static final int SET_SUBSCRIPTION_ID_STATUS_UNKNOWN = -1;
+
/** @hide */
@IntDef(prefix = "SET_SUBSCRIPTION_ID_STATUS_",
value = {
@@ -1121,6 +1129,7 @@
SET_SUBSCRIPTION_ID_STATUS_FAILED_INVALID_SUBSCRIPTION_ID,
SET_SUBSCRIPTION_ID_STATUS_FAILED_INTERNAL_ERROR,
SET_SUBSCRIPTION_ID_STATUS_FAILED_NOT_SUPPORTED,
+ SET_SUBSCRIPTION_ID_STATUS_UNKNOWN
})
@Retention(RetentionPolicy.SOURCE)
public @interface SetSubscriptionIdStatus {}
@@ -1129,9 +1138,10 @@
* Sets the system's default NFC subscription id.
*
* <p> For devices with multiple UICC/EUICC that is configured to be NFCEE, this sets the
- * default UICC NFCEE that will handle NFC offhost CE transactoions </p>
+ * default UICC NFCEE that will handle NFC offhost CE transactions </p>
*
- * @param subscriptionId the default NFC subscription Id to set.
+ * @param subscriptionId the default NFC subscription Id to set. User can get subscription id
+ * from {@link SubscriptionManager#getSubscriptionId(int)}
* @return status of the operation.
*
* @throws UnsupportedOperationException If the device does not have
@@ -1153,7 +1163,7 @@
* Returns the system's default NFC subscription id.
*
* <p> For devices with multiple UICC/EUICC that is configured to be NFCEE, this returns the
- * default UICC NFCEE that will handle NFC offhost CE transactoions </p>
+ * default UICC NFCEE that will handle NFC offhost CE transactions </p>
* <p> If the device has no UICC that can serve as NFCEE, this will return
* {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.</p>
*
diff --git a/nfc/lint-baseline.xml b/nfc/lint-baseline.xml
index dd7b03d..67b496e 100644
--- a/nfc/lint-baseline.xml
+++ b/nfc/lint-baseline.xml
@@ -2,215 +2,6 @@
<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
<issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `new android.nfc.cardemulation.AidGroup`"
- errorLine1=" AidGroup aidGroup = new AidGroup(aids, category);"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="377"
- column="29"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.AidGroup#getAids`"
- errorLine1=" return (group != null ? group.getAids() : null);"
- errorLine2=" ~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="537"
- column="43"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.AidGroup#getAids`"
- errorLine1=" return (group != null ? group.getAids() : null);"
- errorLine2=" ~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="547"
- column="47"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getAids`"
- errorLine1=" return (serviceInfo != null ? serviceInfo.getAids() : null);"
- errorLine2=" ~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="714"
- column="55"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getAids`"
- errorLine1=" return (serviceInfo != null ? serviceInfo.getAids() : null);"
- errorLine2=" ~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="724"
- column="59"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#isOnHost`"
- errorLine1=" if (!serviceInfo.isOnHost()) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="755"
- column="34"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`"
- errorLine1=" return serviceInfo.getOffHostSecureElement() == null ?"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="756"
- column="40"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`"
- errorLine1=' "OffHost" : serviceInfo.getOffHostSecureElement();'
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="757"
- column="53"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#isOnHost`"
- errorLine1=" if (!serviceInfo.isOnHost()) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="772"
- column="38"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`"
- errorLine1=" return serviceInfo.getOffHostSecureElement() == null ?"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="773"
- column="44"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`"
- errorLine1=' "Offhost" : serviceInfo.getOffHostSecureElement();'
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="774"
- column="57"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getDescription`"
- errorLine1=" return (serviceInfo != null ? serviceInfo.getDescription() : null);"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="798"
- column="55"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getDescription`"
- errorLine1=" return (serviceInfo != null ? serviceInfo.getDescription() : null);"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="808"
- column="59"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`"
- errorLine1=" if (!activity.isResumed()) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="1032"
- column="23"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`"
- errorLine1=" if (!activity.isResumed()) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
- line="1066"
- column="23"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`"
- errorLine1=" resumed = activity.isResumed();"
- errorLine2=" ~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/NfcActivityManager.java"
- line="124"
- column="32"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`"
- errorLine1=" if (!activity.isResumed()) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/NfcAdapter.java"
- line="2457"
- column="23"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`"
- errorLine1=" if (!activity.isResumed()) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/NfcFCardEmulation.java"
- line="315"
- column="23"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`"
- errorLine1=" if (!activity.isResumed()) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="frameworks/base/nfc/java/android/nfc/cardemulation/NfcFCardEmulation.java"
- line="351"
- column="23"/>
- </issue>
-
- <issue
id="FlaggedApi"
message="Method `NfcOemExtension()` is a flagged API and should be inside an `if (Flags.nfcOemExtension())` check (or annotate the surrounding method `NfcAdapter` with `@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) to transfer requirement to caller`)"
errorLine1=" mNfcOemExtension = new NfcOemExtension(mContext, this);"
@@ -287,4 +78,4 @@
column="44"/>
</issue>
-</issues>
\ No newline at end of file
+</issues>
diff --git a/nfc/tests/Android.bp b/nfc/tests/Android.bp
index bfa814d..b6090e8 100644
--- a/nfc/tests/Android.bp
+++ b/nfc/tests/Android.bp
@@ -25,17 +25,36 @@
android_test {
name: "NfcManagerTests",
static_libs: [
- "androidx.test.ext.junit",
+ "androidx.test.core",
"androidx.test.rules",
- "mockito-target-minus-junit4",
+ "androidx.test.runner",
+ "androidx.test.ext.junit",
+ "framework-nfc.impl",
+ "mockito-target-extended-minus-junit4",
+ "frameworks-base-testutils",
"truth",
+ "androidx.annotation_annotation",
+ "androidx.appcompat_appcompat",
+ "flag-junit",
+ "platform-test-annotations",
+ "testables",
],
libs: [
- "framework-nfc.impl",
+ "android.test.base.stubs.system",
+ "android.test.mock.stubs.system",
"android.test.runner.stubs.system",
],
+ jni_libs: [
+ // Required for ExtendedMockito
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
srcs: ["src/**/*.java"],
platform_apis: true,
certificate: "platform",
- test_suites: ["device-tests"],
+ test_suites: [
+ "device-tests",
+ "mts-nfc",
+ ],
+ min_sdk_version: "35", // Should be 36 later.
}
diff --git a/nfc/tests/AndroidManifest.xml b/nfc/tests/AndroidManifest.xml
index 99e2c34c..9564672 100644
--- a/nfc/tests/AndroidManifest.xml
+++ b/nfc/tests/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.nfc">
- <application>
+ <application android:debuggable="true">
<uses-library android:name="android.test.runner" />
</application>
diff --git a/packages/CrashRecovery/framework/Android.bp b/packages/CrashRecovery/framework/Android.bp
index 43d8a62..1a3446ec 100644
--- a/packages/CrashRecovery/framework/Android.bp
+++ b/packages/CrashRecovery/framework/Android.bp
@@ -14,7 +14,11 @@
name: "framework-platformcrashrecovery",
srcs: [":framework-crashrecovery-sources"],
defaults: ["framework-non-updatable-unbundled-defaults"],
- permitted_packages: ["android.service.watchdog"],
+ permitted_packages: [
+ "android.service.watchdog",
+ "android.crashrecovery",
+ ],
+ static_libs: ["android.crashrecovery.flags-aconfig-java"],
aidl: {
include_dirs: [
"frameworks/base/core/java",
diff --git a/packages/CrashRecovery/framework/api/system-current.txt b/packages/CrashRecovery/framework/api/system-current.txt
index 3a48a4a..68429ea 100644
--- a/packages/CrashRecovery/framework/api/system-current.txt
+++ b/packages/CrashRecovery/framework/api/system-current.txt
@@ -9,7 +9,9 @@
method @NonNull public abstract java.util.List<java.lang.String> onGetRequestedPackages();
method @NonNull public abstract java.util.List<android.service.watchdog.ExplicitHealthCheckService.PackageConfig> onGetSupportedPackages();
method public abstract void onRequestHealthCheck(@NonNull String);
+ method @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") public final void setHealthCheckResultCallback(@Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<android.os.Bundle>);
field public static final String BIND_PERMISSION = "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
+ field @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") public static final String EXTRA_HEALTH_CHECK_PASSED_PACKAGE = "android.service.watchdog.extra.HEALTH_CHECK_PASSED_PACKAGE";
field public static final String SERVICE_INTERFACE = "android.service.watchdog.ExplicitHealthCheckService";
}
diff --git a/packages/CrashRecovery/framework/api/test-current.txt b/packages/CrashRecovery/framework/api/test-current.txt
index 54f501f..d802177 100644
--- a/packages/CrashRecovery/framework/api/test-current.txt
+++ b/packages/CrashRecovery/framework/api/test-current.txt
@@ -1,9 +1 @@
// Signature format: 2.0
-package android.service.watchdog {
-
- public abstract class ExplicitHealthCheckService extends android.app.Service {
- method public void setCallback(@Nullable android.os.RemoteCallback);
- }
-
-}
-
diff --git a/packages/CrashRecovery/framework/java/android/service/watchdog/ExplicitHealthCheckService.java b/packages/CrashRecovery/framework/java/android/service/watchdog/ExplicitHealthCheckService.java
index 7befbfb..b03e376 100644
--- a/packages/CrashRecovery/framework/java/android/service/watchdog/ExplicitHealthCheckService.java
+++ b/packages/CrashRecovery/framework/java/android/service/watchdog/ExplicitHealthCheckService.java
@@ -18,15 +18,17 @@
import static android.os.Parcelable.Creator;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.crashrecovery.flags.Flags;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -42,7 +44,9 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
/**
* A service to provide packages supporting explicit health checks and route checks to these
@@ -89,11 +93,10 @@
/**
* {@link Bundle} key for a {@link String} value.
- *
- * {@hide}
*/
+ @FlaggedApi(Flags.FLAG_ENABLE_CRASHRECOVERY)
public static final String EXTRA_HEALTH_CHECK_PASSED_PACKAGE =
- "android.service.watchdog.extra.health_check_passed_package";
+ "android.service.watchdog.extra.HEALTH_CHECK_PASSED_PACKAGE";
/**
* The Intent action that a service must respond to. Add it to the intent filter of the service
@@ -152,7 +155,8 @@
@NonNull public abstract List<String> onGetRequestedPackages();
private final Handler mHandler = Handler.createAsync(Looper.getMainLooper());
- @Nullable private RemoteCallback mCallback;
+ @Nullable private Consumer<Bundle> mHealthCheckResultCallback;
+ @Nullable private Executor mCallbackExecutor;
@Override
@NonNull
@@ -161,30 +165,49 @@
}
/**
- * Sets {@link RemoteCallback}, for testing purpose.
+ * Sets a callback to be invoked when an explicit health check passes for a package.
+ * <p>
+ * The callback will receive a {@link Bundle} containing the package name that passed the
+ * health check, identified by the key {@link #EXTRA_HEALTH_CHECK_PASSED_PACKAGE}.
+ * <p>
+ * <b>Note:</b> This API is primarily intended for testing purposes. Calling this outside of a
+ * test environment will override the default callback mechanism used to notify the system
+ * about health check results. Use with caution in production code.
*
- * @hide
+ * @param executor The executor on which the callback should be invoked. If {@code null}, the
+ * callback will be executed on the main thread.
+ * @param callback A callback that receives a {@link Bundle} containing the package name that
+ * passed the health check.
*/
- @TestApi
- public void setCallback(@Nullable RemoteCallback callback) {
- mCallback = callback;
+ @FlaggedApi(Flags.FLAG_ENABLE_CRASHRECOVERY)
+ public final void setHealthCheckResultCallback(@CallbackExecutor @Nullable Executor executor,
+ @Nullable Consumer<Bundle> callback) {
+ mCallbackExecutor = executor;
+ mHealthCheckResultCallback = callback;
}
+
+ private void executeCallback(@NonNull String packageName) {
+ if (mHealthCheckResultCallback != null) {
+ Objects.requireNonNull(packageName,
+ "Package passing explicit health check must be non-null");
+ Bundle bundle = new Bundle();
+ bundle.putString(EXTRA_HEALTH_CHECK_PASSED_PACKAGE, packageName);
+ mHealthCheckResultCallback.accept(bundle);
+ } else {
+ Log.wtf(TAG, "System missed explicit health check result for " + packageName);
+ }
+ }
+
/**
* Implementors should call this to notify the system when explicit health check passes
* for {@code packageName};
*/
public final void notifyHealthCheckPassed(@NonNull String packageName) {
- mHandler.post(() -> {
- if (mCallback != null) {
- Objects.requireNonNull(packageName,
- "Package passing explicit health check must be non-null");
- Bundle bundle = new Bundle();
- bundle.putString(EXTRA_HEALTH_CHECK_PASSED_PACKAGE, packageName);
- mCallback.sendResult(bundle);
- } else {
- Log.wtf(TAG, "System missed explicit health check result for " + packageName);
- }
- });
+ if (mCallbackExecutor != null) {
+ mCallbackExecutor.execute(() -> executeCallback(packageName));
+ } else {
+ mHandler.post(() -> executeCallback(packageName));
+ }
}
/**
@@ -296,9 +319,7 @@
private class ExplicitHealthCheckServiceWrapper extends IExplicitHealthCheckService.Stub {
@Override
public void setCallback(RemoteCallback callback) throws RemoteException {
- mHandler.post(() -> {
- mCallback = callback;
- });
+ mHandler.post(() -> mHealthCheckResultCallback = callback::sendResult);
}
@Override
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
index 2ba93f1..8b8ab58 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
@@ -25,6 +25,7 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -91,6 +92,7 @@
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
/**
@@ -336,20 +338,27 @@
/**
* Registers {@code observer} to listen for package failures. Add a new ObserverInternal for
* this observer if it does not already exist.
+ * For executing mitigations observers will receive callback on the given executor.
*
* <p>Observers are expected to call this on boot. It does not specify any packages but
* it will resume observing any packages requested from a previous boot.
- * @hide
+ *
+ * @param observer instance of {@link PackageHealthObserver} for observing package failures
+ * and boot loops.
+ * @param executor Executor for the thread on which observers would receive callbacks
*/
- public void registerHealthObserver(PackageHealthObserver observer) {
+ public void registerHealthObserver(@NonNull PackageHealthObserver observer,
+ @NonNull @CallbackExecutor Executor executor) {
synchronized (sLock) {
ObserverInternal internalObserver = mAllObservers.get(observer.getUniqueIdentifier());
if (internalObserver != null) {
internalObserver.registeredObserver = observer;
+ internalObserver.observerExecutor = executor;
} else {
internalObserver = new ObserverInternal(observer.getUniqueIdentifier(),
new ArrayList<>());
internalObserver.registeredObserver = observer;
+ internalObserver.observerExecutor = executor;
mAllObservers.put(observer.getUniqueIdentifier(), internalObserver);
syncState("added new observer");
}
@@ -357,40 +366,53 @@
}
/**
- * Starts observing the health of the {@code packages} for {@code observer} and notifies
- * {@code observer} of any package failures within the monitoring duration.
+ * Starts observing the health of the {@code packages} for {@code observer}.
+ * Note: Observer needs to be registered with {@link #registerHealthObserver} before calling
+ * this API.
*
* <p>If monitoring a package supporting explicit health check, at the end of the monitoring
* duration if {@link #onHealthCheckPassed} was never called,
- * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} will be called as if the package failed.
+ * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} will be called as if the
+ * package failed.
*
* <p>If {@code observer} is already monitoring a package in {@code packageNames},
* the monitoring window of that package will be reset to {@code durationMs} and the health
- * check state will be reset to a default depending on if the package is contained in
- * {@link mPackagesWithExplicitHealthCheckEnabled}.
+ * check state will be reset to a default.
*
- * <p>If {@code packageNames} is empty, this will be a no-op.
+ * <p>The {@code observer} must be registered with {@link #registerHealthObserver} before
+ * calling this method.
*
- * <p>If {@code durationMs} is less than 1, a default monitoring duration
- * {@link #DEFAULT_OBSERVING_DURATION_MS} will be used.
- * @hide
+ * @param packageNames The list of packages to check. If this is empty, the call will be a
+ * no-op.
+ *
+ * @param timeoutMs The timeout after which Explicit Health Checks would not run. If this is
+ * less than 1, a default monitoring duration 2 days will be used.
+ *
+ * @throws IllegalStateException if the observer was not previously registered
*/
- public void startObservingHealth(PackageHealthObserver observer, List<String> packageNames,
- long durationMs) {
+ public void startExplicitHealthCheck(@NonNull PackageHealthObserver observer,
+ @NonNull List<String> packageNames, long timeoutMs) {
+ synchronized (sLock) {
+ if (!mAllObservers.containsKey(observer.getUniqueIdentifier())) {
+ Slog.wtf(TAG, "No observer found, need to register the observer: "
+ + observer.getUniqueIdentifier());
+ throw new IllegalStateException("Observer not registered");
+ }
+ }
if (packageNames.isEmpty()) {
Slog.wtf(TAG, "No packages to observe, " + observer.getUniqueIdentifier());
return;
}
- if (durationMs < 1) {
- Slog.wtf(TAG, "Invalid duration " + durationMs + "ms for observer "
+ if (timeoutMs < 1) {
+ Slog.wtf(TAG, "Invalid duration " + timeoutMs + "ms for observer "
+ observer.getUniqueIdentifier() + ". Not observing packages " + packageNames);
- durationMs = DEFAULT_OBSERVING_DURATION_MS;
+ timeoutMs = DEFAULT_OBSERVING_DURATION_MS;
}
List<MonitoredPackage> packages = new ArrayList<>();
for (int i = 0; i < packageNames.size(); i++) {
// Health checks not available yet so health check state will start INACTIVE
- MonitoredPackage pkg = newMonitoredPackage(packageNames.get(i), durationMs, false);
+ MonitoredPackage pkg = newMonitoredPackage(packageNames.get(i), timeoutMs, false);
if (pkg != null) {
packages.add(pkg);
} else {
@@ -423,9 +445,6 @@
}
}
- // Register observer in case not already registered
- registerHealthObserver(observer);
-
// Sync after we add the new packages to the observers. We may have received packges
// requiring an earlier schedule than we are currently scheduled for.
syncState("updated observers");
@@ -437,9 +456,8 @@
* Unregisters {@code observer} from listening to package failure.
* Additionally, this stops observing any packages that may have previously been observed
* even from a previous boot.
- * @hide
*/
- public void unregisterHealthObserver(PackageHealthObserver observer) {
+ public void unregisterHealthObserver(@NonNull PackageHealthObserver observer) {
mLongTaskHandler.post(() -> {
synchronized (sLock) {
mAllObservers.remove(observer.getUniqueIdentifier());
@@ -485,7 +503,7 @@
for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
VersionedPackage versionedPackage = packages.get(pIndex);
// Observer that will receive failure for versionedPackage
- PackageHealthObserver currentObserverToNotify = null;
+ ObserverInternal currentObserverToNotify = null;
int currentObserverImpact = Integer.MAX_VALUE;
MonitoredPackage currentMonitoredPackage = null;
@@ -506,7 +524,7 @@
versionedPackage, failureReason, mitigationCount);
if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0
&& impact < currentObserverImpact) {
- currentObserverToNotify = registeredObserver;
+ currentObserverToNotify = observer;
currentObserverImpact = impact;
currentMonitoredPackage = p;
}
@@ -515,18 +533,23 @@
// Execute action with least user impact
if (currentObserverToNotify != null) {
- int mitigationCount = 1;
+ int mitigationCount;
if (currentMonitoredPackage != null) {
currentMonitoredPackage.noteMitigationCallLocked();
mitigationCount =
currentMonitoredPackage.getMitigationCountLocked();
+ } else {
+ mitigationCount = 1;
}
if (Flags.recoverabilityDetection()) {
maybeExecute(currentObserverToNotify, versionedPackage,
failureReason, currentObserverImpact, mitigationCount);
} else {
- currentObserverToNotify.onExecuteHealthCheckMitigation(
- versionedPackage, failureReason, mitigationCount);
+ PackageHealthObserver registeredObserver =
+ currentObserverToNotify.registeredObserver;
+ currentObserverToNotify.observerExecutor.execute(() ->
+ registeredObserver.onExecuteHealthCheckMitigation(
+ versionedPackage, failureReason, mitigationCount));
}
}
}
@@ -539,10 +562,11 @@
* For native crashes or explicit health check failures, call directly into each observer to
* mitigate the error without going through failure threshold logic.
*/
+ @GuardedBy("sLock")
private void handleFailureImmediately(List<VersionedPackage> packages,
@FailureReasons int failureReason) {
VersionedPackage failingPackage = packages.size() > 0 ? packages.get(0) : null;
- PackageHealthObserver currentObserverToNotify = null;
+ ObserverInternal currentObserverToNotify = null;
int currentObserverImpact = Integer.MAX_VALUE;
for (ObserverInternal observer: mAllObservers.values()) {
PackageHealthObserver registeredObserver = observer.registeredObserver;
@@ -551,7 +575,7 @@
failingPackage, failureReason, 1);
if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0
&& impact < currentObserverImpact) {
- currentObserverToNotify = registeredObserver;
+ currentObserverToNotify = observer;
currentObserverImpact = impact;
}
}
@@ -561,23 +585,30 @@
maybeExecute(currentObserverToNotify, failingPackage, failureReason,
currentObserverImpact, /*mitigationCount=*/ 1);
} else {
- currentObserverToNotify.onExecuteHealthCheckMitigation(failingPackage,
- failureReason, 1);
+ PackageHealthObserver registeredObserver =
+ currentObserverToNotify.registeredObserver;
+ currentObserverToNotify.observerExecutor.execute(() ->
+ registeredObserver.onExecuteHealthCheckMitigation(failingPackage,
+ failureReason, 1));
+
}
}
}
- private void maybeExecute(PackageHealthObserver currentObserverToNotify,
+ private void maybeExecute(ObserverInternal currentObserverToNotify,
VersionedPackage versionedPackage,
@FailureReasons int failureReason,
int currentObserverImpact,
int mitigationCount) {
if (allowMitigations(currentObserverImpact, versionedPackage)) {
+ PackageHealthObserver registeredObserver;
synchronized (sLock) {
mLastMitigation = mSystemClock.uptimeMillis();
+ registeredObserver = currentObserverToNotify.registeredObserver;
}
- currentObserverToNotify.onExecuteHealthCheckMitigation(versionedPackage, failureReason,
- mitigationCount);
+ currentObserverToNotify.observerExecutor.execute(() ->
+ registeredObserver.onExecuteHealthCheckMitigation(versionedPackage,
+ failureReason, mitigationCount));
}
}
@@ -613,8 +644,7 @@
mBootThreshold.reset();
}
int mitigationCount = mBootThreshold.getMitigationCount() + 1;
- PackageHealthObserver currentObserverToNotify = null;
- ObserverInternal currentObserverInternal = null;
+ ObserverInternal currentObserverToNotify = null;
int currentObserverImpact = Integer.MAX_VALUE;
for (int i = 0; i < mAllObservers.size(); i++) {
final ObserverInternal observer = mAllObservers.valueAt(i);
@@ -626,25 +656,31 @@
: registeredObserver.onBootLoop(mitigationCount);
if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0
&& impact < currentObserverImpact) {
- currentObserverToNotify = registeredObserver;
- currentObserverInternal = observer;
+ currentObserverToNotify = observer;
currentObserverImpact = impact;
}
}
}
+
if (currentObserverToNotify != null) {
+ PackageHealthObserver registeredObserver =
+ currentObserverToNotify.registeredObserver;
if (Flags.recoverabilityDetection()) {
int currentObserverMitigationCount =
- currentObserverInternal.getBootMitigationCount() + 1;
- currentObserverInternal.setBootMitigationCount(
+ currentObserverToNotify.getBootMitigationCount() + 1;
+ currentObserverToNotify.setBootMitigationCount(
currentObserverMitigationCount);
saveAllObserversBootMitigationCountToMetadata(METADATA_FILE);
- currentObserverToNotify.onExecuteBootLoopMitigation(
- currentObserverMitigationCount);
+ currentObserverToNotify.observerExecutor
+ .execute(() -> registeredObserver.onExecuteBootLoopMitigation(
+ currentObserverMitigationCount));
} else {
mBootThreshold.setMitigationCount(mitigationCount);
mBootThreshold.saveMitigationCountToMetadata();
- currentObserverToNotify.onExecuteBootLoopMitigation(mitigationCount);
+ currentObserverToNotify.observerExecutor
+ .execute(() -> registeredObserver.onExecuteBootLoopMitigation(
+ mitigationCount));
+
}
}
}
@@ -734,19 +770,19 @@
* The minimum value that can be returned by any observer.
* It represents that no mitigations were available.
*/
- public static final int LEAST_PACKAGE_HEALTH_OBSERVER_IMPACT =
+ public static final int USER_IMPACT_THRESHOLD_NONE =
PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
/**
* The mitigation impact beyond which the user will start noticing the mitigations.
*/
- public static final int MEDIUM_USER_IMPACT_THRESHOLD =
+ public static final int USER_IMPACT_THRESHOLD_MEDIUM =
PackageHealthObserverImpact.USER_IMPACT_LEVEL_20;
/**
* The mitigation impact beyond which the user impact is severely high.
*/
- public static final int HIGH_USER_IMPACT_THRESHOLD =
+ public static final int USER_IMPACT_THRESHOLD_HIGH =
PackageHealthObserverImpact.USER_IMPACT_LEVEL_71;
/**
@@ -791,11 +827,9 @@
public interface PackageHealthObserver {
/**
* Called when health check fails for the {@code versionedPackage}.
- *
- * Note: if the returned user impact is higher than
- * {@link #DEFAULT_HIGH_USER_IMPACT_THRESHOLD}, then
- * {@link #onExecuteHealthCheckMitigation} would be called only in severe device conditions
- * like boot-loop or network failure.
+ * Note: if the returned user impact is higher than {@link #USER_IMPACT_THRESHOLD_HIGH},
+ * then {@link #onExecuteHealthCheckMitigation} would be called only in severe device
+ * conditions like boot-loop or network failure.
*
* @param versionedPackage the package that is failing. This may be null if a native
* service is crashing.
@@ -803,9 +837,9 @@
* @param mitigationCount the number of times mitigation has been called for this package
* (including this time).
*
- *
- * @return any value greater than {@link #LEAST_PACKAGE_HEALTH_OBSERVER_IMPACT} to express
- * the impact of mitigation on the user in {@link #onExecuteHealthCheckMitigation}
+ * @return any value greater than {@link #USER_IMPACT_THRESHOLD_NONE} to express
+ * the impact of mitigation on the user in {@link #onExecuteHealthCheckMitigation}.
+ * Returning {@link #USER_IMPACT_THRESHOLD_NONE} would indicate no mitigations available.
*/
@PackageHealthObserverImpact int onHealthCheckFailed(
@Nullable VersionedPackage versionedPackage,
@@ -835,8 +869,9 @@
* @param mitigationCount the number of times mitigation has been attempted for this
* boot loop (including this time).
*
- * @return any value greater than {@link #LEAST_PACKAGE_HEALTH_OBSERVER_IMPACT} to express
- * the impact of mitigation on the user in {@link #onExecuteBootLoopMitigation}
+ * @return any value greater than {@link #USER_IMPACT_THRESHOLD_NONE} to express
+ * the impact of mitigation on the user in {@link #onExecuteBootLoopMitigation}.
+ * Returning {@link #USER_IMPACT_THRESHOLD_NONE} would indicate no mitigations available.
*/
default @PackageHealthObserverImpact int onBootLoop(int mitigationCount) {
return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
@@ -879,7 +914,7 @@
* passed to observers in these API.
*
* <p> A persistent observer may choose to start observing certain failing packages, even if
- * it has not explicitly asked to watch the package with {@link #startObservingHealth}.
+ * it has not explicitly asked to watch the package with {@link #startExplicitHealthCheck}.
*/
default boolean mayObservePackage(@NonNull String packageName) {
return false;
@@ -1136,8 +1171,10 @@
if (versionedPkg != null) {
Slog.i(TAG,
"Explicit health check failed for package " + versionedPkg);
- registeredObserver.onExecuteHealthCheckMitigation(versionedPkg,
- PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK, 1);
+ observer.observerExecutor.execute(() ->
+ registeredObserver.onExecuteHealthCheckMitigation(versionedPkg,
+ PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK,
+ 1));
}
}
}
@@ -1395,6 +1432,7 @@
@Nullable
@GuardedBy("sLock")
public PackageHealthObserver registeredObserver;
+ public Executor observerExecutor;
private int mMitigationCount;
ObserverInternal(String name, List<MonitoredPackage> packages) {
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
index 992f581..bad6ab7 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
@@ -160,7 +160,7 @@
/** Register the Rescue Party observer as a Package Watchdog health observer */
public static void registerHealthObserver(Context context) {
PackageWatchdog.getInstance(context).registerHealthObserver(
- RescuePartyObserver.getInstance(context));
+ RescuePartyObserver.getInstance(context), context.getMainExecutor());
}
private static boolean isDisabled() {
@@ -313,7 +313,7 @@
callingPackageList.addAll(callingPackages);
Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: "
+ updatedNamespace);
- PackageWatchdog.getInstance(context).startObservingHealth(
+ PackageWatchdog.getInstance(context).startExplicitHealthCheck(
rescuePartyObserver,
callingPackageList,
DEFAULT_OBSERVING_DURATION_MS);
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 311def8..c80a1a4 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -111,7 +111,8 @@
dataDir.mkdirs();
mLastStagedRollbackIdsFile = new File(dataDir, "last-staged-rollback-ids");
mTwoPhaseRollbackEnabledFile = new File(dataDir, "two-phase-rollback-enabled");
- PackageWatchdog.getInstance(mContext).registerHealthObserver(this);
+ PackageWatchdog.getInstance(mContext).registerHealthObserver(this,
+ context.getMainExecutor());
if (SystemProperties.getBoolean("sys.boot_completed", false)) {
// Load the value from the file if system server has crashed and restarted
@@ -273,16 +274,6 @@
Preconditions.checkState(mHandler.getLooper().isCurrentThread());
}
- /**
- * Start observing health of {@code packages} for {@code durationMs}.
- * This may cause {@code packages} to be rolled back if they crash too freqeuntly.
- */
- @AnyThread
- @NonNull
- public void startObservingHealth(@NonNull List<String> packages, @NonNull long durationMs) {
- PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs);
- }
-
@AnyThread
@NonNull
public void notifyRollbackAvailable(@NonNull RollbackInfo rollback) {
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
index 88fe36c..4fea937 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
@@ -87,6 +87,7 @@
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
/**
@@ -362,7 +363,7 @@
* it will resume observing any packages requested from a previous boot.
* @hide
*/
- public void registerHealthObserver(PackageHealthObserver observer) {
+ public void registerHealthObserver(PackageHealthObserver observer, Executor ignoredExecutor) {
synchronized (mLock) {
ObserverInternal internalObserver = mAllObservers.get(observer.getUniqueIdentifier());
if (internalObserver != null) {
@@ -396,7 +397,7 @@
* {@link #DEFAULT_OBSERVING_DURATION_MS} will be used.
* @hide
*/
- public void startObservingHealth(PackageHealthObserver observer, List<String> packageNames,
+ public void startExplicitHealthCheck(PackageHealthObserver observer, List<String> packageNames,
long durationMs) {
if (packageNames.isEmpty()) {
Slog.wtf(TAG, "No packages to observe, " + observer.getUniqueIdentifier());
@@ -445,7 +446,7 @@
}
// Register observer in case not already registered
- registerHealthObserver(observer);
+ registerHealthObserver(observer, null);
// Sync after we add the new packages to the observers. We may have received packges
// requiring an earlier schedule than we are currently scheduled for.
@@ -861,7 +862,7 @@
* otherwise
*
* <p> A persistent observer may choose to start observing certain failing packages, even if
- * it has not explicitly asked to watch the package with {@link #startObservingHealth}.
+ * it has not explicitly asked to watch the package with {@link #startExplicitHealthCheck}.
*/
default boolean mayObservePackage(@NonNull String packageName) {
return false;
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java
index f757236..2bb72fb 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java
@@ -166,7 +166,7 @@
/** Register the Rescue Party observer as a Package Watchdog health observer */
public static void registerHealthObserver(Context context) {
PackageWatchdog.getInstance(context).registerHealthObserver(
- RescuePartyObserver.getInstance(context));
+ RescuePartyObserver.getInstance(context), null);
}
private static boolean isDisabled() {
@@ -387,7 +387,7 @@
callingPackageList.addAll(callingPackages);
Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: "
+ updatedNamespace);
- PackageWatchdog.getInstance(context).startObservingHealth(
+ PackageWatchdog.getInstance(context).startExplicitHealthCheck(
rescuePartyObserver,
callingPackageList,
DEFAULT_OBSERVING_DURATION_MS);
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 7445534..0692cdb 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -110,7 +110,7 @@
dataDir.mkdirs();
mLastStagedRollbackIdsFile = new File(dataDir, "last-staged-rollback-ids");
mTwoPhaseRollbackEnabledFile = new File(dataDir, "two-phase-rollback-enabled");
- PackageWatchdog.getInstance(mContext).registerHealthObserver(this);
+ PackageWatchdog.getInstance(mContext).registerHealthObserver(this, null);
mApexManager = apexManager;
if (SystemProperties.getBoolean("sys.boot_completed", false)) {
@@ -284,7 +284,7 @@
@AnyThread
@NonNull
public void startObservingHealth(@NonNull List<String> packages, @NonNull long durationMs) {
- PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs);
+ PackageWatchdog.getInstance(mContext).startExplicitHealthCheck(this, packages, durationMs);
}
@AnyThread
diff --git a/packages/CredentialManager/res/values-hy/strings.xml b/packages/CredentialManager/res/values-hy/strings.xml
index 630a08a..7997127 100644
--- a/packages/CredentialManager/res/values-hy/strings.xml
+++ b/packages/CredentialManager/res/values-hy/strings.xml
@@ -22,7 +22,7 @@
<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_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>
diff --git a/packages/CredentialManager/wear/res/values-hi/strings.xml b/packages/CredentialManager/wear/res/values-hi/strings.xml
index a061453..405d3f8 100644
--- a/packages/CredentialManager/wear/res/values-hi/strings.xml
+++ b/packages/CredentialManager/wear/res/values-hi/strings.xml
@@ -22,8 +22,8 @@
<string name="use_password_title" msgid="4655101984031246476">"क्या आपको पासवर्ड का इस्तेमाल करना है?"</string>
<string name="dialog_dismiss_button" msgid="989567669882005067">"खारिज करें"</string>
<string name="dialog_continue_button" msgid="8630290044077052145">"जारी रखें"</string>
- <string name="dialog_sign_in_options_button" msgid="448002958902615054">"साइन इन करने के विकल्प"</string>
- <string name="sign_in_options_title" msgid="6720572645638986680">"साइन इन करने के विकल्प"</string>
+ <string name="dialog_sign_in_options_button" msgid="448002958902615054">"साइन-इन करने के विकल्प"</string>
+ <string name="sign_in_options_title" msgid="6720572645638986680">"साइन-इन करने के विकल्प"</string>
<string name="provider_list_title" msgid="6803918216129492212">"साइन-इन क्रेडेंशियल मैनेज करें"</string>
<string name="choose_sign_in_title" msgid="3616025924746872202">"साइन इन करने का कोई तरीका चुनें"</string>
<string name="choose_passkey_title" msgid="8459270617632817465">"कोई पासकी चुनें"</string>
diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
index 6a4bb21..a3b06e8 100644
--- a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
@@ -16,8 +16,10 @@
package com.android.localtransport;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.backup.BackupAgent;
+import android.app.backup.BackupAnnotations;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupManagerMonitor;
@@ -52,6 +54,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
/**
* Backup transport for stashing stuff into a known location on disk, and
@@ -939,4 +942,15 @@
}
}
}
+
+ @NonNull
+ @Override
+ public List<String> getPackagesThatShouldNotUseRestrictedMode(
+ @NonNull List<String> packageNames,
+ @BackupAnnotations.OperationType int operationType) {
+ if (DEBUG) {
+ Log.d(TAG, "No restricted mode packages: " + mParameters.noRestrictedModePackages());
+ }
+ return mParameters.noRestrictedModePackages();
+ }
}
diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
index aaa18bf..c980913 100644
--- a/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
@@ -16,26 +16,33 @@
package com.android.localtransport;
-import android.util.KeyValueSettingObserver;
import android.content.ContentResolver;
import android.os.Handler;
import android.provider.Settings;
import android.util.KeyValueListParser;
+import android.util.KeyValueSettingObserver;
+
+import java.util.Arrays;
+import java.util.List;
public class LocalTransportParameters extends KeyValueSettingObserver {
- private static final String TAG = "LocalTransportParams";
private static final String SETTING = Settings.Secure.BACKUP_LOCAL_TRANSPORT_PARAMETERS;
private static final String KEY_FAKE_ENCRYPTION_FLAG = "fake_encryption_flag";
private static final String KEY_NON_INCREMENTAL_ONLY = "non_incremental_only";
private static final String KEY_IS_DEVICE_TRANSFER = "is_device_transfer";
private static final String KEY_IS_ENCRYPTED = "is_encrypted";
private static final String KEY_LOG_AGENT_RESULTS = "log_agent_results";
+ // This needs to be a list of package names separated by semicolons. For example:
+ // "com.package1;com.package2;com.package3". We can't use commas because the base class uses
+ // commas to split Key/Value pairs.
+ private static final String KEY_NO_RESTRICTED_MODE_PACKAGES = "no_restricted_mode_packages";
private boolean mFakeEncryptionFlag;
private boolean mIsNonIncrementalOnly;
private boolean mIsDeviceTransfer;
private boolean mIsEncrypted;
private boolean mLogAgentResults;
+ private String mNoRestrictedModePackages;
public LocalTransportParameters(Handler handler, ContentResolver resolver) {
super(handler, resolver, Settings.Secure.getUriFor(SETTING));
@@ -61,6 +68,13 @@
return mLogAgentResults;
}
+ List<String> noRestrictedModePackages() {
+ if (mNoRestrictedModePackages == null) {
+ return List.of();
+ }
+ return Arrays.stream(mNoRestrictedModePackages.split(";")).toList();
+ }
+
public String getSettingValue(ContentResolver resolver) {
return Settings.Secure.getString(resolver, SETTING);
}
@@ -71,5 +85,6 @@
mIsDeviceTransfer = parser.getBoolean(KEY_IS_DEVICE_TRANSFER, false);
mIsEncrypted = parser.getBoolean(KEY_IS_ENCRYPTED, false);
mLogAgentResults = parser.getBoolean(KEY_LOG_AGENT_RESULTS, /* def */ false);
+ mNoRestrictedModePackages = parser.getString(KEY_NO_RESTRICTED_MODE_PACKAGES, /* def */ "");
}
}
diff --git a/packages/NeuralNetworks/framework/Android.bp b/packages/NeuralNetworks/framework/Android.bp
new file mode 100644
index 0000000..6f45daa
--- /dev/null
+++ b/packages/NeuralNetworks/framework/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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+filegroup {
+ name: "framework-ondeviceintelligence-sources",
+ srcs: [
+ "java/**/*.aidl",
+ "java/**/*.java",
+ ],
+ path: "java",
+ visibility: [
+ "//frameworks/base:__subpackages__",
+ "//packages/modules/NeuralNetworks:__subpackages__",
+ ],
+}
diff --git a/core/java/android/app/ondeviceintelligence/DownloadCallback.java b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/DownloadCallback.java
similarity index 96%
rename from core/java/android/app/ondeviceintelligence/DownloadCallback.java
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/DownloadCallback.java
index 30c6e19..95fb288 100644
--- a/core/java/android/app/ondeviceintelligence/DownloadCallback.java
+++ b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/DownloadCallback.java
@@ -23,8 +23,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.PersistableBundle;
-
-import androidx.annotation.IntDef;
+import android.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -53,14 +52,14 @@
/**
* Sent when feature download has been initiated already, hence no need to request download
- * again. Caller can query {@link OnDeviceIntelligenceManager#getFeatureStatus} to check if
+ * again. Caller can query {@link OnDeviceIntelligenceManager#getFeatureDetails} to check if
* download has been completed.
*/
int DOWNLOAD_FAILURE_STATUS_DOWNLOADING = 3;
/**
* Sent when feature download did not start due to errors (e.g. remote exception of features not
- * available). Caller can query {@link OnDeviceIntelligenceManager#getFeatureStatus} to check
+ * available). Caller can query {@link OnDeviceIntelligenceManager#getFeatureDetails} to check
* if feature-status is {@link FeatureDetails#FEATURE_STATUS_DOWNLOADABLE}.
*/
int DOWNLOAD_FAILURE_STATUS_UNAVAILABLE = 4;
@@ -72,7 +71,7 @@
DOWNLOAD_FAILURE_STATUS_NETWORK_FAILURE,
DOWNLOAD_FAILURE_STATUS_DOWNLOADING,
DOWNLOAD_FAILURE_STATUS_UNAVAILABLE
- }, open = true)
+ })
@Retention(RetentionPolicy.SOURCE)
@interface DownloadFailureStatus {
}
diff --git a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/Feature.aidl
similarity index 93%
rename from core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/Feature.aidl
index 0589bf8..47cfb4a 100644
--- a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
+++ b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/Feature.aidl
@@ -19,4 +19,4 @@
/**
* @hide
*/
-parcelable FeatureDetails;
+@JavaOnlyStableParcelable parcelable Feature;
diff --git a/core/java/android/app/ondeviceintelligence/Feature.java b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/Feature.java
similarity index 96%
rename from core/java/android/app/ondeviceintelligence/Feature.java
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/Feature.java
index bcc56073..88f4de29 100644
--- a/core/java/android/app/ondeviceintelligence/Feature.java
+++ b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/Feature.java
@@ -26,6 +26,8 @@
import android.os.Parcelable;
import android.os.PersistableBundle;
+import java.util.Objects;
+
/**
* Represents a typical feature associated with on-device intelligence.
*
@@ -56,9 +58,8 @@
this.mModelName = modelName;
this.mType = type;
this.mVariant = variant;
- this.mFeatureParams = featureParams;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mFeatureParams);
+ this.mFeatureParams = Objects.requireNonNull(featureParams,
+ "featureParams should be non-null.");
}
/** Returns the unique and immutable identifier of this feature. */
@@ -167,8 +168,6 @@
this.mType = type;
this.mVariant = variant;
this.mFeatureParams = featureParams;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mFeatureParams);
}
public static final @NonNull Parcelable.Creator<Feature> CREATOR
@@ -200,6 +199,7 @@
/**
* Provides a builder instance to create a feature for given id.
+ *
* @param id the unique identifier for the feature.
*/
public Builder(int id) {
diff --git a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/FeatureDetails.aidl
similarity index 92%
copy from core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
copy to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/FeatureDetails.aidl
index 0589bf8..c5b3532 100644
--- a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
+++ b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/FeatureDetails.aidl
@@ -19,4 +19,4 @@
/**
* @hide
*/
-parcelable FeatureDetails;
+@JavaOnlyStableParcelable parcelable FeatureDetails;
diff --git a/core/java/android/app/ondeviceintelligence/FeatureDetails.java b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/FeatureDetails.java
similarity index 87%
rename from core/java/android/app/ondeviceintelligence/FeatureDetails.java
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/FeatureDetails.java
index 44930f2..063cfb8 100644
--- a/core/java/android/app/ondeviceintelligence/FeatureDetails.java
+++ b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/FeatureDetails.java
@@ -19,18 +19,18 @@
import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcelable;
import android.os.PersistableBundle;
-import androidx.annotation.IntDef;
-
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.text.MessageFormat;
+import java.util.Objects;
/**
* Represents a status of a requested {@link Feature}.
@@ -69,7 +69,7 @@
FEATURE_STATUS_DOWNLOADING,
FEATURE_STATUS_AVAILABLE,
FEATURE_STATUS_SERVICE_UNAVAILABLE
- }, open = true)
+ })
@Target({ElementType.TYPE_USE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface Status {
@@ -79,18 +79,12 @@
@Status int featureStatus,
@NonNull PersistableBundle featureDetailParams) {
this.mFeatureStatus = featureStatus;
- com.android.internal.util.AnnotationValidations.validate(
- Status.class, null, mFeatureStatus);
- this.mFeatureDetailParams = featureDetailParams;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mFeatureDetailParams);
+ this.mFeatureDetailParams = Objects.requireNonNull(featureDetailParams);
}
public FeatureDetails(
@Status int featureStatus) {
this.mFeatureStatus = featureStatus;
- com.android.internal.util.AnnotationValidations.validate(
- Status.class, null, mFeatureStatus);
this.mFeatureDetailParams = new PersistableBundle();
}
@@ -155,11 +149,7 @@
PersistableBundle.CREATOR);
this.mFeatureStatus = status;
- com.android.internal.util.AnnotationValidations.validate(
- Status.class, null, mFeatureStatus);
this.mFeatureDetailParams = persistableBundle;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mFeatureDetailParams);
}
diff --git a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ICancellationSignal.aidl
similarity index 82%
copy from core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
copy to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ICancellationSignal.aidl
index 0589bf8..1fe201f 100644
--- a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
+++ b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ICancellationSignal.aidl
@@ -5,7 +5,7 @@
* you may not use this 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,
@@ -17,6 +17,8 @@
package android.app.ondeviceintelligence;
/**
- * @hide
- */
-parcelable FeatureDetails;
+ * @hide
+ */
+oneway interface ICancellationSignal {
+ void cancel();
+}
diff --git a/core/java/android/app/ondeviceintelligence/IDownloadCallback.aidl b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IDownloadCallback.aidl
similarity index 100%
rename from core/java/android/app/ondeviceintelligence/IDownloadCallback.aidl
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IDownloadCallback.aidl
diff --git a/core/java/android/app/ondeviceintelligence/IFeatureCallback.aidl b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IFeatureCallback.aidl
similarity index 100%
rename from core/java/android/app/ondeviceintelligence/IFeatureCallback.aidl
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IFeatureCallback.aidl
diff --git a/core/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl
similarity index 100%
rename from core/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl
diff --git a/core/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl
similarity index 100%
rename from core/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl
diff --git a/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
similarity index 97%
rename from core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
index 1977a39..fac5ec6 100644
--- a/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
+++ b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
@@ -16,8 +16,8 @@
package android.app.ondeviceintelligence;
- import com.android.internal.infra.AndroidFuture;
- import android.os.ICancellationSignal;
+ import com.android.modules.utils.AndroidFuture;
+ import android.app.ondeviceintelligence.ICancellationSignal;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
diff --git a/core/java/android/app/ondeviceintelligence/IProcessingSignal.aidl b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IProcessingSignal.aidl
similarity index 100%
rename from core/java/android/app/ondeviceintelligence/IProcessingSignal.aidl
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IProcessingSignal.aidl
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IRemoteCallback.aidl b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IRemoteCallback.aidl
new file mode 100644
index 0000000..6f07693
--- /dev/null
+++ b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IRemoteCallback.aidl
@@ -0,0 +1,24 @@
+/*
+* 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.
+*/
+
+package android.app.ondeviceintelligence;
+
+import android.os.Bundle;
+
+/* @hide */
+oneway interface IRemoteCallback {
+ void sendResult(in Bundle data);
+}
diff --git a/core/java/android/app/ondeviceintelligence/IResponseCallback.aidl b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IResponseCallback.aidl
similarity index 100%
rename from core/java/android/app/ondeviceintelligence/IResponseCallback.aidl
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IResponseCallback.aidl
diff --git a/core/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl
similarity index 100%
rename from core/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl
diff --git a/core/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl
similarity index 100%
rename from core/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ITokenInfoCallback.aidl
diff --git a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/InferenceInfo.aidl
similarity index 92%
copy from core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
copy to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/InferenceInfo.aidl
index 0589bf8..6f63254 100644
--- a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
+++ b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/InferenceInfo.aidl
@@ -19,4 +19,4 @@
/**
* @hide
*/
-parcelable FeatureDetails;
+@JavaOnlyStableParcelable parcelable InferenceInfo;
diff --git a/core/java/android/app/ondeviceintelligence/InferenceInfo.java b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/InferenceInfo.java
similarity index 100%
rename from core/java/android/app/ondeviceintelligence/InferenceInfo.java
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/InferenceInfo.java
diff --git a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java
similarity index 97%
rename from core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java
index 03ff563a..2881c9d 100644
--- a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java
+++ b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/OnDeviceIntelligenceException.java
@@ -20,13 +20,14 @@
import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.PersistableBundle;
-import androidx.annotation.IntDef;
-
import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
@@ -124,8 +125,9 @@
PROCESSING_ERROR_SERVICE_UNAVAILABLE,
ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
PROCESSING_UPDATE_STATUS_CONNECTION_FAILED
- }, open = true)
+ })
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+ @Retention(RetentionPolicy.SOURCE)
@interface OnDeviceIntelligenceError {
}
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/OnDeviceIntelligenceFrameworkInitializer.java b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/OnDeviceIntelligenceFrameworkInitializer.java
new file mode 100644
index 0000000..7d35dd7
--- /dev/null
+++ b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/OnDeviceIntelligenceFrameworkInitializer.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
+import android.app.SystemServiceRegistry;
+import android.content.Context;
+
+/**
+ * Class for performing registration for OnDeviceIntelligence service.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public class OnDeviceIntelligenceFrameworkInitializer {
+ private OnDeviceIntelligenceFrameworkInitializer() {
+ }
+
+ /**
+ * Called by {@link SystemServiceRegistry}'s static initializer and registers
+ * OnDeviceIntelligence service to {@link Context}, so that {@link Context#getSystemService} can
+ * return them.
+ *
+ * @throws IllegalStateException if this is called from anywhere besides {@link
+ * SystemServiceRegistry}
+ */
+ public static void registerServiceWrappers() {
+ SystemServiceRegistry.registerContextAwareService(Context.ON_DEVICE_INTELLIGENCE_SERVICE,
+ OnDeviceIntelligenceManager.class,
+ (context, serviceBinder) -> {
+ IOnDeviceIntelligenceManager manager =
+ IOnDeviceIntelligenceManager.Stub.asInterface(serviceBinder);
+ return new OnDeviceIntelligenceManager(context, manager);
+ });
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
similarity index 88%
rename from core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
index 91651e3..dc0665a 100644
--- a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
+++ b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
@@ -23,18 +23,18 @@
import android.annotation.CallbackExecutor;
import android.annotation.CurrentTimeMillisLong;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.app.ondeviceintelligence.utils.BinderUtils;
import android.content.Context;
import android.graphics.Bitmap;
-import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.IBinder;
-import android.os.ICancellationSignal;
import android.os.OutcomeReceiver;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
@@ -42,9 +42,7 @@
import android.system.OsConstants;
import android.util.Log;
-import androidx.annotation.IntDef;
-
-import com.android.internal.infra.AndroidFuture;
+import com.android.modules.utils.AndroidFuture;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -80,10 +78,39 @@
public static final String AUGMENT_REQUEST_CONTENT_BUNDLE_KEY =
"AugmentRequestContentBundleKey";
+ /**
+ * Timeout to be used for unbinding to the configured remote {@link
+ * android.service.ondeviceintelligence.OnDeviceIntelligenceService} if there are no requests in
+ * the queue. A value of -1 represents to never unbind.
+ *
+ * @hide
+ */
+ public static final String ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS =
+ "on_device_intelligence_unbind_timeout_ms";
+
+ /**
+ * Timeout that represents maximum idle time before which a callback should be populated.
+ *
+ * @hide
+ */
+ public static final String ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS =
+ "on_device_intelligence_idle_timeout_ms";
+
+ /**
+ * Timeout to be used for unbinding to the configured remote {@link
+ * android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService} if there are no
+ * requests in the queue. A value of -1 represents to never unbind.
+ *
+ * @hide
+ */
+ public static final String ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS =
+ "on_device_inference_unbind_timeout_ms";
+
private static final String TAG = "OnDeviceIntelligence";
private final Context mContext;
private final IOnDeviceIntelligenceManager mService;
+
/**
* @hide
*/
@@ -105,11 +132,11 @@
try {
RemoteCallback callback = new RemoteCallback(result -> {
if (result == null) {
- Binder.withCleanCallingIdentity(
+ BinderUtils.withCleanCallingIdentity(
() -> callbackExecutor.execute(() -> versionConsumer.accept(0)));
}
long version = result.getLong(API_VERSION_BUNDLE_KEY);
- Binder.withCleanCallingIdentity(
+ BinderUtils.withCleanCallingIdentity(
() -> callbackExecutor.execute(() -> versionConsumer.accept(version)));
});
mService.getVersion(callback);
@@ -151,14 +178,14 @@
new IFeatureCallback.Stub() {
@Override
public void onSuccess(Feature result) {
- Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
() -> featureReceiver.onResult(result)));
}
@Override
public void onFailure(int errorCode, String errorMessage,
PersistableBundle errorParams) {
- Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
() -> featureReceiver.onError(
new OnDeviceIntelligenceException(
errorCode, errorMessage, errorParams))));
@@ -185,14 +212,14 @@
new IListFeaturesCallback.Stub() {
@Override
public void onSuccess(List<Feature> result) {
- Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
() -> featureListReceiver.onResult(result)));
}
@Override
public void onFailure(int errorCode, String errorMessage,
PersistableBundle errorParams) {
- Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
() -> featureListReceiver.onError(
new OnDeviceIntelligenceException(
errorCode, errorMessage, errorParams))));
@@ -223,14 +250,14 @@
@Override
public void onSuccess(FeatureDetails result) {
- Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
() -> featureDetailsReceiver.onResult(result)));
}
@Override
public void onFailure(int errorCode, String errorMessage,
PersistableBundle errorParams) {
- Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
() -> featureDetailsReceiver.onError(
new OnDeviceIntelligenceException(errorCode,
errorMessage, errorParams))));
@@ -268,27 +295,27 @@
@Override
public void onDownloadStarted(long bytesToDownload) {
- Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
() -> callback.onDownloadStarted(bytesToDownload)));
}
@Override
public void onDownloadProgress(long bytesDownloaded) {
- Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
() -> callback.onDownloadProgress(bytesDownloaded)));
}
@Override
public void onDownloadFailed(int failureStatus, String errorMessage,
PersistableBundle errorParams) {
- Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
() -> callback.onDownloadFailed(failureStatus, errorMessage,
errorParams)));
}
@Override
public void onDownloadCompleted(PersistableBundle downloadParams) {
- Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
() -> callback.onDownloadCompleted(downloadParams)));
}
};
@@ -325,14 +352,14 @@
ITokenInfoCallback callback = new ITokenInfoCallback.Stub() {
@Override
public void onSuccess(TokenInfo tokenInfo) {
- Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
() -> outcomeReceiver.onResult(tokenInfo)));
}
@Override
public void onFailure(int errorCode, String errorMessage,
PersistableBundle errorParams) {
- Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
() -> outcomeReceiver.onError(
new OnDeviceIntelligenceException(
errorCode, errorMessage, errorParams))));
@@ -377,7 +404,7 @@
IResponseCallback callback = new IResponseCallback.Stub() {
@Override
public void onSuccess(@InferenceParams Bundle result) {
- Binder.withCleanCallingIdentity(() -> {
+ BinderUtils.withCleanCallingIdentity(() -> {
callbackExecutor.execute(() -> processingCallback.onResult(result));
});
}
@@ -385,7 +412,7 @@
@Override
public void onFailure(int errorCode, String errorMessage,
PersistableBundle errorParams) {
- Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
() -> processingCallback.onError(
new OnDeviceIntelligenceException(
errorCode, errorMessage, errorParams))));
@@ -394,7 +421,7 @@
@Override
public void onDataAugmentRequest(@NonNull @InferenceParams Bundle request,
@NonNull RemoteCallback contentCallback) {
- Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
() -> processingCallback.onDataAugmentRequest(request, result -> {
Bundle bundle = new Bundle();
bundle.putParcelable(AUGMENT_REQUEST_CONTENT_BUNDLE_KEY, result);
@@ -447,7 +474,7 @@
IStreamingResponseCallback callback = new IStreamingResponseCallback.Stub() {
@Override
public void onNewContent(@InferenceParams Bundle result) {
- Binder.withCleanCallingIdentity(() -> {
+ BinderUtils.withCleanCallingIdentity(() -> {
callbackExecutor.execute(
() -> streamingProcessingCallback.onPartialResult(result));
});
@@ -455,7 +482,7 @@
@Override
public void onSuccess(@InferenceParams Bundle result) {
- Binder.withCleanCallingIdentity(() -> {
+ BinderUtils.withCleanCallingIdentity(() -> {
callbackExecutor.execute(
() -> streamingProcessingCallback.onResult(result));
});
@@ -464,7 +491,7 @@
@Override
public void onFailure(int errorCode, String errorMessage,
PersistableBundle errorParams) {
- Binder.withCleanCallingIdentity(() -> {
+ BinderUtils.withCleanCallingIdentity(() -> {
callbackExecutor.execute(
() -> streamingProcessingCallback.onError(
new OnDeviceIntelligenceException(
@@ -476,7 +503,7 @@
@Override
public void onDataAugmentRequest(@NonNull @InferenceParams Bundle content,
@NonNull RemoteCallback contentCallback) {
- Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
() -> streamingProcessingCallback.onDataAugmentRequest(content,
contentResponse -> {
Bundle bundle = new Bundle();
@@ -537,7 +564,7 @@
REQUEST_TYPE_INFERENCE,
REQUEST_TYPE_PREPARE,
REQUEST_TYPE_EMBEDDINGS
- }, open = true)
+ })
@Target({ElementType.TYPE_USE, ElementType.METHOD, ElementType.PARAMETER,
ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
@@ -614,8 +641,17 @@
if (error != null || cancellationTransport == null) {
Log.e(TAG, "Unable to receive the remote cancellation signal.", error);
} else {
- cancellationSignal.setRemote(
- ICancellationSignal.Stub.asInterface(cancellationTransport));
+ ICancellationSignal remoteCancellationSignal =
+ ICancellationSignal.Stub.asInterface(cancellationTransport);
+ cancellationSignal.setOnCancelListener(
+ () -> {
+ try {
+ remoteCancellationSignal.cancel();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to propagate cancellation signal.",
+ e);
+ }
+ });
}
}, callbackExecutor);
return cancellationFuture;
@@ -638,6 +674,4 @@
}, executor);
return processingSignalFuture;
}
-
-
-}
+}
\ No newline at end of file
diff --git a/core/java/android/app/ondeviceintelligence/ProcessingCallback.java b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ProcessingCallback.java
similarity index 100%
rename from core/java/android/app/ondeviceintelligence/ProcessingCallback.java
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ProcessingCallback.java
diff --git a/core/java/android/app/ondeviceintelligence/ProcessingSignal.java b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ProcessingSignal.java
similarity index 100%
rename from core/java/android/app/ondeviceintelligence/ProcessingSignal.java
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/ProcessingSignal.java
diff --git a/core/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java
similarity index 100%
rename from core/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/StreamingProcessingCallback.java
diff --git a/core/java/android/app/ondeviceintelligence/TokenInfo.aidl b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/TokenInfo.aidl
similarity index 93%
rename from core/java/android/app/ondeviceintelligence/TokenInfo.aidl
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/TokenInfo.aidl
index 2c19c1e..599b337 100644
--- a/core/java/android/app/ondeviceintelligence/TokenInfo.aidl
+++ b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/TokenInfo.aidl
@@ -19,4 +19,4 @@
/**
* @hide
*/
-parcelable TokenInfo;
+@JavaOnlyStableParcelable parcelable TokenInfo;
diff --git a/core/java/android/app/ondeviceintelligence/TokenInfo.java b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/TokenInfo.java
similarity index 100%
rename from core/java/android/app/ondeviceintelligence/TokenInfo.java
rename to packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/TokenInfo.java
diff --git a/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/utils/BinderUtils.java b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/utils/BinderUtils.java
new file mode 100644
index 0000000..2916f03
--- /dev/null
+++ b/packages/NeuralNetworks/framework/java/android/app/ondeviceintelligence/utils/BinderUtils.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 android.app.ondeviceintelligence.utils;
+
+import android.annotation.NonNull;
+import android.os.Binder;
+
+import java.util.function.Supplier;
+
+/**
+ * Collection of utilities for {@link Binder} and related classes.
+ * @hide
+ */
+public class BinderUtils {
+ /**
+ * Convenience method for running the provided action enclosed in
+ * {@link Binder#clearCallingIdentity}/{@link Binder#restoreCallingIdentity}
+ *
+ * Any exception thrown by the given action will be caught and rethrown after the call to
+ * {@link Binder#restoreCallingIdentity}
+ *
+ * Note that this is copied from Binder#withCleanCallingIdentity with minor changes
+ * since it is not public.
+ *
+ * @hide
+ */
+ public static final <T extends Exception> void withCleanCallingIdentity(
+ @NonNull ThrowingRunnable<T> action) throws T {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ action.run();
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ /**
+ * Like a Runnable, but declared to throw an exception.
+ *
+ * @param <T> The exception class which is declared to be thrown.
+ */
+ @FunctionalInterface
+ public interface ThrowingRunnable<T extends Exception> {
+ /** @see java.lang.Runnable */
+ void run() throws T;
+ }
+
+ /**
+ * Convenience method for running the provided action enclosed in
+ * {@link Binder#clearCallingIdentity}/{@link Binder#restoreCallingIdentity} returning the
+ * result.
+ *
+ * <p>Any exception thrown by the given action will be caught and rethrown after
+ * the call to {@link Binder#restoreCallingIdentity}.
+ *
+ * Note that this is copied from Binder#withCleanCallingIdentity with minor changes
+ * since it is not public.
+ *
+ * @hide
+ */
+ public static final <T, E extends Exception> T withCleanCallingIdentity(
+ @NonNull ThrowingSupplier<T, E> action) throws E {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ return action.get();
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ /**
+ * An equivalent of {@link Supplier}
+ *
+ * @param <T> The class which is declared to be returned.
+ * @param <E> The exception class which is declared to be thrown.
+ */
+ @FunctionalInterface
+ public interface ThrowingSupplier<T, E extends Exception> {
+ /** @see java.util.function.Supplier */
+ T get() throws E;
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl b/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
similarity index 95%
rename from core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
rename to packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
index 45c4350..cba18c1 100644
--- a/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
+++ b/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
@@ -18,14 +18,14 @@
import android.os.PersistableBundle;
import android.os.ParcelFileDescriptor;
-import android.os.ICancellationSignal;
+import android.app.ondeviceintelligence.ICancellationSignal;
import android.os.RemoteCallback;
import android.app.ondeviceintelligence.IDownloadCallback;
import android.app.ondeviceintelligence.Feature;
import android.app.ondeviceintelligence.IFeatureCallback;
import android.app.ondeviceintelligence.IListFeaturesCallback;
import android.app.ondeviceintelligence.IFeatureDetailsCallback;
-import com.android.internal.infra.AndroidFuture;
+import com.android.modules.utils.AndroidFuture;
import android.service.ondeviceintelligence.IRemoteProcessingService;
diff --git a/core/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl b/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl
similarity index 93%
rename from core/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl
rename to packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl
index 1af3b0f..504fdd9 100644
--- a/core/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl
+++ b/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl
@@ -21,11 +21,11 @@
import android.app.ondeviceintelligence.ITokenInfoCallback;
import android.app.ondeviceintelligence.IProcessingSignal;
import android.app.ondeviceintelligence.Feature;
-import android.os.IRemoteCallback;
-import android.os.ICancellationSignal;
+import android.app.ondeviceintelligence.IRemoteCallback;
+import android.app.ondeviceintelligence.ICancellationSignal;
import android.os.PersistableBundle;
import android.os.Bundle;
-import com.android.internal.infra.AndroidFuture;
+import com.android.modules.utils.AndroidFuture;
import android.service.ondeviceintelligence.IRemoteStorageService;
import android.service.ondeviceintelligence.IProcessingUpdateStatusCallback;
diff --git a/core/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl b/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl
similarity index 100%
rename from core/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl
rename to packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl
diff --git a/core/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl b/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl
similarity index 100%
rename from core/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl
rename to packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl
diff --git a/core/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl b/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
similarity index 95%
rename from core/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
rename to packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
index a6f49e1..253df89 100644
--- a/core/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
+++ b/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
@@ -20,7 +20,7 @@
import android.os.ParcelFileDescriptor;
import android.os.RemoteCallback;
-import com.android.internal.infra.AndroidFuture;
+import com.android.modules.utils.AndroidFuture;
/**
* Interface for a concrete implementation to provide access to storage read access
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java b/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
similarity index 67%
rename from core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
rename to packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
index d82fe1c..6907e2b 100644
--- a/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
+++ b/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
@@ -18,8 +18,6 @@
import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
import android.annotation.CallSuper;
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
@@ -27,11 +25,11 @@
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.app.Service;
import android.app.ondeviceintelligence.DownloadCallback;
import android.app.ondeviceintelligence.Feature;
import android.app.ondeviceintelligence.FeatureDetails;
+import android.app.ondeviceintelligence.ICancellationSignal;
import android.app.ondeviceintelligence.IDownloadCallback;
import android.app.ondeviceintelligence.IFeatureCallback;
import android.app.ondeviceintelligence.IFeatureDetailsCallback;
@@ -39,14 +37,14 @@
import android.app.ondeviceintelligence.OnDeviceIntelligenceException;
import android.app.ondeviceintelligence.OnDeviceIntelligenceManager;
import android.app.ondeviceintelligence.OnDeviceIntelligenceManager.StateParams;
+import android.app.ondeviceintelligence.utils.BinderUtils;
import android.content.Intent;
-import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
-import android.os.ICancellationSignal;
import android.os.Looper;
+import android.os.Message;
import android.os.OutcomeReceiver;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
@@ -55,10 +53,11 @@
import android.util.Log;
import android.util.Slog;
-import com.android.internal.infra.AndroidFuture;
+import com.android.modules.utils.AndroidFuture;
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -92,6 +91,18 @@
@SystemApi
@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
public abstract class OnDeviceIntelligenceService extends Service {
+ private static final int MSG_ON_READY = 1;
+ private static final int MSG_GET_VERSION = 2;
+ private static final int MSG_LIST_FEATURES = 3;
+ private static final int MSG_GET_FEATURE = 4;
+ private static final int MSG_GET_FEATURE_DETAILS = 5;
+ private static final int MSG_DOWNLOAD_FEATURE = 6;
+ private static final int MSG_GET_READ_ONLY_FILE_DESCRIPTOR = 7;
+ private static final int MSG_GET_READ_ONLY_FEATURE_FILE_DESCRIPTOR_MAP = 8;
+ private static final int MSG_REGISTER_REMOTE_SERVICES = 9;
+ private static final int MSG_INFERENCE_SERVICE_CONNECTED = 10;
+ private static final int MSG_INFERENCE_SERVICE_DISCONNECTED = 11;
+
private static final String TAG = OnDeviceIntelligenceService.class.getSimpleName();
private volatile IRemoteProcessingService mRemoteProcessingService;
@@ -101,19 +112,71 @@
@Override
public void onCreate() {
super.onCreate();
- mHandler = new Handler(Looper.getMainLooper(), null /* callback */, true /* async */);
+ mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ switch (msg.what) {
+ case MSG_ON_READY:
+ OnDeviceIntelligenceService.this.onReady();
+ break;
+ case MSG_GET_VERSION:
+ OnDeviceIntelligenceService.this.onGetVersion(
+ (LongConsumer) msg.obj);
+ break;
+ case MSG_LIST_FEATURES:
+ OnDeviceIntelligenceService.this.onListFeatures(
+ msg.arg1,
+ (OutcomeReceiver<List<Feature>, OnDeviceIntelligenceException>) msg.obj);
+ break;
+ case MSG_GET_FEATURE:
+ GetFeatureParams params = (GetFeatureParams) msg.obj;
+ OnDeviceIntelligenceService.this.onGetFeature(
+ msg.arg1,
+ msg.arg2,
+ params.callback);
+ break;
+ case MSG_GET_FEATURE_DETAILS:
+ FeatureDetailsParams detailsParams = (FeatureDetailsParams) msg.obj;
+ OnDeviceIntelligenceService.this.onGetFeatureDetails(
+ msg.arg1,
+ detailsParams.feature,
+ detailsParams.callback);
+ break;
+ case MSG_DOWNLOAD_FEATURE:
+ DownloadParams downloadParams = (DownloadParams) msg.obj;
+ OnDeviceIntelligenceService.this.onDownloadFeature(
+ msg.arg1,
+ downloadParams.feature,
+ downloadParams.cancellationSignal,
+ downloadParams.callback);
+ break;
+ case MSG_GET_READ_ONLY_FILE_DESCRIPTOR:
+ FileDescriptorParams fdParams = (FileDescriptorParams) msg.obj;
+ OnDeviceIntelligenceService.this.onGetReadOnlyFileDescriptor(
+ fdParams.fileName,
+ fdParams.future);
+ break;
+ case MSG_GET_READ_ONLY_FEATURE_FILE_DESCRIPTOR_MAP:
+ FeatureFileDescriptorParams ffdParams =
+ (FeatureFileDescriptorParams) msg.obj;
+ OnDeviceIntelligenceService.this.onGetReadOnlyFeatureFileDescriptorMap(
+ ffdParams.feature,
+ ffdParams.consumer);
+ break;
+ case MSG_REGISTER_REMOTE_SERVICES:
+ mRemoteProcessingService = (IRemoteProcessingService) msg.obj;
+ break;
+ case MSG_INFERENCE_SERVICE_CONNECTED:
+ OnDeviceIntelligenceService.this.onInferenceServiceConnected();
+ break;
+ case MSG_INFERENCE_SERVICE_DISCONNECTED:
+ OnDeviceIntelligenceService.this.onInferenceServiceDisconnected();
+ break;
+ }
+ }
+ };
}
- /**
- * The {@link Intent} that must be declared as handled by the service. To be supported, the
- * service must also require the
- * {@link android.Manifest.permission#BIND_ON_DEVICE_INTELLIGENCE_SERVICE}
- * permission so that other applications can not abuse it.
- */
- @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
- public static final String SERVICE_INTERFACE =
- "android.service.ondeviceintelligence.OnDeviceIntelligenceService";
-
/**
* @hide
@@ -126,45 +189,37 @@
/** {@inheritDoc} */
@Override
public void ready() {
- mHandler.executeOrSendMessage(
- obtainMessage(OnDeviceIntelligenceService::onReady,
- OnDeviceIntelligenceService.this));
+ mHandler.sendEmptyMessage(MSG_ON_READY);
}
@Override
public void getVersion(RemoteCallback remoteCallback) {
Objects.requireNonNull(remoteCallback);
- mHandler.executeOrSendMessage(
- obtainMessage(
- OnDeviceIntelligenceService::onGetVersion,
- OnDeviceIntelligenceService.this, l -> {
- Bundle b = new Bundle();
- b.putLong(
- OnDeviceIntelligenceManager.API_VERSION_BUNDLE_KEY,
- l);
- remoteCallback.sendResult(b);
- }));
+ Message msg = Message.obtain(mHandler, MSG_GET_VERSION,
+ (LongConsumer) (l -> {
+ Bundle b = new Bundle();
+ b.putLong(OnDeviceIntelligenceManager.API_VERSION_BUNDLE_KEY, l);
+ remoteCallback.sendResult(b);
+ }));
+ mHandler.sendMessage(msg);
}
@Override
public void listFeatures(int callerUid,
IListFeaturesCallback listFeaturesCallback) {
Objects.requireNonNull(listFeaturesCallback);
- mHandler.executeOrSendMessage(
- obtainMessage(
- OnDeviceIntelligenceService::onListFeatures,
- OnDeviceIntelligenceService.this, callerUid,
- wrapListFeaturesCallback(listFeaturesCallback)));
+ Message msg = Message.obtain(mHandler, MSG_LIST_FEATURES,
+ callerUid, 0, wrapListFeaturesCallback(listFeaturesCallback));
+ mHandler.sendMessage(msg);
}
@Override
public void getFeature(int callerUid, int id, IFeatureCallback featureCallback) {
Objects.requireNonNull(featureCallback);
- mHandler.executeOrSendMessage(
- obtainMessage(
- OnDeviceIntelligenceService::onGetFeature,
- OnDeviceIntelligenceService.this, callerUid,
- id, wrapFeatureCallback(featureCallback)));
+ Message msg = Message.obtain(mHandler, MSG_GET_FEATURE,
+ callerUid, id,
+ new GetFeatureParams(wrapFeatureCallback(featureCallback)));
+ mHandler.sendMessage(msg);
}
@@ -173,11 +228,11 @@
IFeatureDetailsCallback featureDetailsCallback) {
Objects.requireNonNull(feature);
Objects.requireNonNull(featureDetailsCallback);
- mHandler.executeOrSendMessage(
- obtainMessage(
- OnDeviceIntelligenceService::onGetFeatureDetails,
- OnDeviceIntelligenceService.this, callerUid,
- feature, wrapFeatureDetailsCallback(featureDetailsCallback)));
+ Message msg = Message.obtain(mHandler, MSG_GET_FEATURE_DETAILS,
+ new FeatureDetailsParams(feature,
+ wrapFeatureDetailsCallback(featureDetailsCallback)));
+ msg.arg1 = callerUid;
+ mHandler.sendMessage(msg);
}
@Override
@@ -186,18 +241,24 @@
IDownloadCallback downloadCallback) {
Objects.requireNonNull(feature);
Objects.requireNonNull(downloadCallback);
- ICancellationSignal transport = null;
+
+ CancellationSignal cancellationSignal = new CancellationSignal();
if (cancellationSignalFuture != null) {
- transport = CancellationSignal.createTransport();
+ ICancellationSignal transport = new ICancellationSignal.Stub() {
+ @Override
+ public void cancel() {
+ cancellationSignal.cancel();
+ }
+ };
cancellationSignalFuture.complete(transport);
}
- mHandler.executeOrSendMessage(
- obtainMessage(
- OnDeviceIntelligenceService::onDownloadFeature,
- OnDeviceIntelligenceService.this, callerUid,
- feature,
- CancellationSignal.fromTransport(transport),
+
+ Message msg = Message.obtain(mHandler, MSG_DOWNLOAD_FEATURE,
+ new DownloadParams(feature,
+ cancellationSignalFuture != null ? cancellationSignal : null,
wrapDownloadCallback(downloadCallback)));
+ msg.arg1 = callerUid;
+ mHandler.sendMessage(msg);
}
@Override
@@ -205,11 +266,9 @@
AndroidFuture<ParcelFileDescriptor> future) {
Objects.requireNonNull(fileName);
Objects.requireNonNull(future);
- mHandler.executeOrSendMessage(
- obtainMessage(
- OnDeviceIntelligenceService::onGetReadOnlyFileDescriptor,
- OnDeviceIntelligenceService.this, fileName,
- future));
+ Message msg = Message.obtain(mHandler, MSG_GET_READ_ONLY_FILE_DESCRIPTOR,
+ new FileDescriptorParams(fileName, future));
+ mHandler.sendMessage(msg);
}
@Override
@@ -217,16 +276,15 @@
Feature feature, RemoteCallback remoteCallback) {
Objects.requireNonNull(feature);
Objects.requireNonNull(remoteCallback);
- mHandler.executeOrSendMessage(
- obtainMessage(
- OnDeviceIntelligenceService::onGetReadOnlyFeatureFileDescriptorMap,
- OnDeviceIntelligenceService.this, feature,
- parcelFileDescriptorMap -> {
- Bundle bundle = new Bundle();
- parcelFileDescriptorMap.forEach(bundle::putParcelable);
- remoteCallback.sendResult(bundle);
- tryClosePfds(parcelFileDescriptorMap.values());
- }));
+ Message msg = Message.obtain(mHandler,
+ MSG_GET_READ_ONLY_FEATURE_FILE_DESCRIPTOR_MAP,
+ new FeatureFileDescriptorParams(feature, parcelFileDescriptorMap -> {
+ Bundle bundle = new Bundle();
+ parcelFileDescriptorMap.forEach(bundle::putParcelable);
+ remoteCallback.sendResult(bundle);
+ tryClosePfds(parcelFileDescriptorMap.values());
+ }));
+ mHandler.sendMessage(msg);
}
@Override
@@ -237,18 +295,12 @@
@Override
public void notifyInferenceServiceConnected() {
- mHandler.executeOrSendMessage(
- obtainMessage(
- OnDeviceIntelligenceService::onInferenceServiceConnected,
- OnDeviceIntelligenceService.this));
+ mHandler.sendEmptyMessage(MSG_INFERENCE_SERVICE_CONNECTED);
}
@Override
public void notifyInferenceServiceDisconnected() {
- mHandler.executeOrSendMessage(
- obtainMessage(
- OnDeviceIntelligenceService::onInferenceServiceDisconnected,
- OnDeviceIntelligenceService.this));
+ mHandler.sendEmptyMessage(MSG_INFERENCE_SERVICE_DISCONNECTED);
}
};
}
@@ -257,13 +309,77 @@
}
/**
+ * The {@link Intent} that must be declared as handled by the service. To be supported, the
+ * service must also require the
+ * {@link android.Manifest.permission#BIND_ON_DEVICE_INTELLIGENCE_SERVICE}
+ * permission so that other applications can not abuse it.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE =
+ "android.service.ondeviceintelligence.OnDeviceIntelligenceService";
+
+ // Parameter holder classes
+ private static class GetFeatureParams {
+ final OutcomeReceiver<Feature, OnDeviceIntelligenceException> callback;
+
+ GetFeatureParams(OutcomeReceiver<Feature, OnDeviceIntelligenceException> callback) {
+ this.callback = callback;
+ }
+ }
+
+ private static class FeatureDetailsParams {
+ final Feature feature;
+ final OutcomeReceiver<FeatureDetails, OnDeviceIntelligenceException> callback;
+
+ FeatureDetailsParams(Feature feature,
+ OutcomeReceiver<FeatureDetails, OnDeviceIntelligenceException> callback) {
+ this.feature = feature;
+ this.callback = callback;
+ }
+ }
+
+ private static class DownloadParams {
+ final Feature feature;
+ final CancellationSignal cancellationSignal;
+ final DownloadCallback callback;
+
+ DownloadParams(Feature feature, CancellationSignal cancellationSignal,
+ DownloadCallback callback) {
+ this.feature = feature;
+ this.cancellationSignal = cancellationSignal;
+ this.callback = callback;
+ }
+ }
+
+ private static class FileDescriptorParams {
+ final String fileName;
+ final AndroidFuture<ParcelFileDescriptor> future;
+
+ FileDescriptorParams(String fileName, AndroidFuture<ParcelFileDescriptor> future) {
+ this.fileName = fileName;
+ this.future = future;
+ }
+ }
+
+ private static class FeatureFileDescriptorParams {
+ final Feature feature;
+ final Consumer<Map<String, ParcelFileDescriptor>> consumer;
+
+ FeatureFileDescriptorParams(Feature feature,
+ Consumer<Map<String, ParcelFileDescriptor>> consumer) {
+ this.feature = feature;
+ this.consumer = consumer;
+ }
+ }
+
+ /**
* Using this signal to assertively a signal each time service binds successfully, used only in
* tests to get a signal that service instance is ready. This is needed because we cannot rely
* on {@link #onCreate} or {@link #onBind} to be invoke on each binding.
*
* @hide
*/
- @TestApi
+ @SystemApi
public void onReady() {
}
@@ -306,7 +422,7 @@
new IProcessingUpdateStatusCallback.Stub() {
@Override
public void onSuccess(PersistableBundle result) {
- Binder.withCleanCallingIdentity(() -> {
+ BinderUtils.withCleanCallingIdentity(() -> {
callbackExecutor.execute(
() -> statusReceiver.onResult(result));
});
@@ -314,7 +430,7 @@
@Override
public void onFailure(int errorCode, String errorMessage) {
- Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ BinderUtils.withCleanCallingIdentity(() -> callbackExecutor.execute(
() -> statusReceiver.onError(
new OnDeviceIntelligenceException(
errorCode, errorMessage))));
@@ -459,7 +575,7 @@
private void onGetReadOnlyFileDescriptor(@NonNull String fileName,
@NonNull AndroidFuture<ParcelFileDescriptor> future) {
Slog.v(TAG, "onGetReadOnlyFileDescriptor " + fileName);
- Binder.withCleanCallingIdentity(() -> {
+ BinderUtils.withCleanCallingIdentity(() -> {
Slog.v(TAG,
"onGetReadOnlyFileDescriptor: " + fileName + " under internal app storage.");
File f = new File(getBaseContext().getFilesDir(), fileName);
@@ -476,7 +592,11 @@
} finally {
future.complete(pfd);
if (pfd != null) {
- pfd.close();
+ try {
+ pfd.close();
+ } catch (IOException e) {
+ Log.w(TAG, "Error closing FD", e);
+ }
}
}
});
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java b/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
similarity index 74%
rename from core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
rename to packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
index 3181556..315dbaf 100644
--- a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
+++ b/packages/NeuralNetworks/framework/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
@@ -19,10 +19,8 @@
import static android.app.ondeviceintelligence.OnDeviceIntelligenceManager.AUGMENT_REQUEST_CONTENT_BUNDLE_KEY;
import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
-import android.annotation.CallbackExecutor;
import android.annotation.CallSuper;
+import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -31,7 +29,9 @@
import android.annotation.SystemApi;
import android.app.Service;
import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.ICancellationSignal;
import android.app.ondeviceintelligence.IProcessingSignal;
+import android.app.ondeviceintelligence.IRemoteCallback;
import android.app.ondeviceintelligence.IResponseCallback;
import android.app.ondeviceintelligence.IStreamingResponseCallback;
import android.app.ondeviceintelligence.ITokenInfoCallback;
@@ -48,11 +48,9 @@
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.IBinder;
-import android.os.ICancellationSignal;
-import android.os.IRemoteCallback;
import android.os.Looper;
+import android.os.Message;
import android.os.OutcomeReceiver;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
@@ -61,7 +59,8 @@
import android.util.Log;
import android.util.Slog;
-import com.android.internal.infra.AndroidFuture;
+import com.android.modules.utils.AndroidFuture;
+import com.android.modules.utils.HandlerExecutor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -100,6 +99,12 @@
public abstract class OnDeviceSandboxedInferenceService extends Service {
private static final String TAG = OnDeviceSandboxedInferenceService.class.getSimpleName();
+ private static final int MSG_TOKEN_INFO_REQUEST = 1;
+ private static final int MSG_PROCESS_REQUEST_STREAMING = 2;
+ private static final int MSG_PROCESS_REQUEST = 3;
+ private static final int MSG_UPDATE_PROCESSING_STATE = 4;
+
+
/**
* @hide
*/
@@ -133,12 +138,12 @@
* @hide
*/
public static final String MODEL_LOADED_BROADCAST_INTENT =
- "android.service.ondeviceintelligence.MODEL_LOADED";
+ "android.service.ondeviceintelligence.MODEL_LOADED";
/**
* @hide
*/
public static final String MODEL_UNLOADED_BROADCAST_INTENT =
- "android.service.ondeviceintelligence.MODEL_UNLOADED";
+ "android.service.ondeviceintelligence.MODEL_UNLOADED";
/**
* @hide
@@ -152,12 +157,115 @@
@Override
public void onCreate() {
super.onCreate();
- mHandler = new Handler(Looper.getMainLooper(), null /* callback */, true /* async */);
+ mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_TOKEN_INFO_REQUEST:
+ TokenInfoParams params = (TokenInfoParams) msg.obj;
+ OnDeviceSandboxedInferenceService.this.onTokenInfoRequest(
+ msg.arg1,
+ params.feature,
+ params.request,
+ params.cancellationSignal,
+ params.callback);
+ break;
+ case MSG_PROCESS_REQUEST_STREAMING:
+ StreamingRequestParams streamParams = (StreamingRequestParams) msg.obj;
+ OnDeviceSandboxedInferenceService.this.onProcessRequestStreaming(
+ msg.arg1,
+ streamParams.feature,
+ streamParams.request,
+ msg.arg2,
+ streamParams.cancellationSignal,
+ streamParams.processingSignal,
+ streamParams.callback);
+ break;
+ case MSG_PROCESS_REQUEST:
+ RequestParams requestParams = (RequestParams) msg.obj;
+ OnDeviceSandboxedInferenceService.this.onProcessRequest(
+ msg.arg1,
+ requestParams.feature,
+ requestParams.request,
+ msg.arg2,
+ requestParams.cancellationSignal,
+ requestParams.processingSignal,
+ requestParams.callback);
+ break;
+ case MSG_UPDATE_PROCESSING_STATE:
+ UpdateStateParams stateParams = (UpdateStateParams) msg.obj;
+ OnDeviceSandboxedInferenceService.this.onUpdateProcessingState(
+ stateParams.processingState,
+ stateParams.callback);
+ break;
+ }
+ }
+ };
}
- /**
- * @hide
- */
+ // Parameter holder classes
+ private static class TokenInfoParams {
+ final Feature feature;
+ final Bundle request;
+ final CancellationSignal cancellationSignal;
+ final OutcomeReceiver<TokenInfo, OnDeviceIntelligenceException> callback;
+
+ TokenInfoParams(Feature feature, Bundle request, CancellationSignal cancellationSignal,
+ OutcomeReceiver<TokenInfo, OnDeviceIntelligenceException> callback) {
+ this.feature = feature;
+ this.request = request;
+ this.cancellationSignal = cancellationSignal;
+ this.callback = callback;
+ }
+ }
+
+ private static class StreamingRequestParams {
+ final Feature feature;
+ final Bundle request;
+ final CancellationSignal cancellationSignal;
+ final ProcessingSignal processingSignal;
+ final StreamingProcessingCallback callback;
+
+ StreamingRequestParams(Feature feature, Bundle request,
+ CancellationSignal cancellationSignal, ProcessingSignal processingSignal,
+ StreamingProcessingCallback callback) {
+ this.feature = feature;
+ this.request = request;
+ this.cancellationSignal = cancellationSignal;
+ this.processingSignal = processingSignal;
+ this.callback = callback;
+ }
+ }
+
+ private static class RequestParams {
+ final Feature feature;
+ final Bundle request;
+ final CancellationSignal cancellationSignal;
+ final ProcessingSignal processingSignal;
+ final ProcessingCallback callback;
+
+ RequestParams(Feature feature, Bundle request,
+ CancellationSignal cancellationSignal, ProcessingSignal processingSignal,
+ ProcessingCallback callback) {
+ this.feature = feature;
+ this.request = request;
+ this.cancellationSignal = cancellationSignal;
+ this.processingSignal = processingSignal;
+ this.callback = callback;
+ }
+ }
+
+ private static class UpdateStateParams {
+ final Bundle processingState;
+ final OutcomeReceiver<PersistableBundle, OnDeviceIntelligenceException> callback;
+
+ UpdateStateParams(Bundle processingState,
+ OutcomeReceiver<PersistableBundle, OnDeviceIntelligenceException> callback) {
+ this.processingState = processingState;
+ this.callback = callback;
+ }
+ }
+
@Nullable
@Override
public final IBinder onBind(@NonNull Intent intent) {
@@ -168,8 +276,7 @@
IRemoteCallback remoteCallback) throws RemoteException {
Objects.requireNonNull(storageService);
mRemoteStorageService = storageService;
- remoteCallback.sendResult(
- Bundle.EMPTY); //to notify caller uid to system-server.
+ remoteCallback.sendResult(Bundle.EMPTY);
}
@Override
@@ -178,34 +285,42 @@
ITokenInfoCallback tokenInfoCallback) {
Objects.requireNonNull(feature);
Objects.requireNonNull(tokenInfoCallback);
- ICancellationSignal transport = null;
+ CancellationSignal cancellationSignal = new CancellationSignal();
if (cancellationSignalFuture != null) {
- transport = CancellationSignal.createTransport();
+ ICancellationSignal transport = new ICancellationSignal.Stub() {
+ @Override
+ public void cancel() {
+ cancellationSignal.cancel();
+ }
+ };
cancellationSignalFuture.complete(transport);
}
- mHandler.executeOrSendMessage(
- obtainMessage(
- OnDeviceSandboxedInferenceService::onTokenInfoRequest,
- OnDeviceSandboxedInferenceService.this,
- callerUid, feature,
- request,
- CancellationSignal.fromTransport(transport),
+ Message msg = Message.obtain(mHandler, MSG_TOKEN_INFO_REQUEST,
+ callerUid, 0,
+ new TokenInfoParams(feature, request,
+ cancellationSignalFuture != null ? cancellationSignal : null,
wrapTokenInfoCallback(tokenInfoCallback)));
+ mHandler.sendMessage(msg);
}
@Override
- public void processRequestStreaming(int callerUid, Feature feature, Bundle request,
- int requestType,
+ public void processRequestStreaming(int callerUid, Feature feature,
+ Bundle request, int requestType,
AndroidFuture cancellationSignalFuture,
AndroidFuture processingSignalFuture,
IStreamingResponseCallback callback) {
Objects.requireNonNull(feature);
Objects.requireNonNull(callback);
- ICancellationSignal transport = null;
+ CancellationSignal cancellationSignal = new CancellationSignal();
if (cancellationSignalFuture != null) {
- transport = CancellationSignal.createTransport();
+ ICancellationSignal transport = new ICancellationSignal.Stub() {
+ @Override
+ public void cancel() {
+ cancellationSignal.cancel();
+ }
+ };
cancellationSignalFuture.complete(transport);
}
IProcessingSignal processingSignalTransport = null;
@@ -214,30 +329,32 @@
processingSignalFuture.complete(processingSignalTransport);
}
-
- mHandler.executeOrSendMessage(
- obtainMessage(
- OnDeviceSandboxedInferenceService::onProcessRequestStreaming,
- OnDeviceSandboxedInferenceService.this, callerUid,
- feature,
- request,
- requestType,
- CancellationSignal.fromTransport(transport),
+ Message msg = Message.obtain(mHandler, MSG_PROCESS_REQUEST_STREAMING,
+ callerUid, requestType,
+ new StreamingRequestParams(feature, request,
+ cancellationSignalFuture != null ? cancellationSignal : null,
ProcessingSignal.fromTransport(processingSignalTransport),
wrapStreamingResponseCallback(callback)));
+ mHandler.sendMessage(msg);
}
@Override
- public void processRequest(int callerUid, Feature feature, Bundle request,
- int requestType,
+ public void processRequest(int callerUid, Feature feature,
+ Bundle request, int requestType,
AndroidFuture cancellationSignalFuture,
AndroidFuture processingSignalFuture,
IResponseCallback callback) {
Objects.requireNonNull(feature);
Objects.requireNonNull(callback);
- ICancellationSignal transport = null;
+
+ CancellationSignal cancellationSignal = new CancellationSignal();
if (cancellationSignalFuture != null) {
- transport = CancellationSignal.createTransport();
+ ICancellationSignal transport = new ICancellationSignal.Stub() {
+ @Override
+ public void cancel() {
+ cancellationSignal.cancel();
+ }
+ };
cancellationSignalFuture.complete(transport);
}
IProcessingSignal processingSignalTransport = null;
@@ -245,14 +362,14 @@
processingSignalTransport = ProcessingSignal.createTransport();
processingSignalFuture.complete(processingSignalTransport);
}
- mHandler.executeOrSendMessage(
- obtainMessage(
- OnDeviceSandboxedInferenceService::onProcessRequest,
- OnDeviceSandboxedInferenceService.this, callerUid, feature,
- request, requestType,
- CancellationSignal.fromTransport(transport),
+
+ Message msg = Message.obtain(mHandler, MSG_PROCESS_REQUEST,
+ callerUid, requestType,
+ new RequestParams(feature, request,
+ cancellationSignalFuture != null ? cancellationSignal : null,
ProcessingSignal.fromTransport(processingSignalTransport),
wrapResponseCallback(callback)));
+ mHandler.sendMessage(msg);
}
@Override
@@ -260,11 +377,11 @@
IProcessingUpdateStatusCallback callback) {
Objects.requireNonNull(processingState);
Objects.requireNonNull(callback);
- mHandler.executeOrSendMessage(
- obtainMessage(
- OnDeviceSandboxedInferenceService::onUpdateProcessingState,
- OnDeviceSandboxedInferenceService.this, processingState,
+
+ Message msg = Message.obtain(mHandler, MSG_UPDATE_PROCESSING_STATE,
+ new UpdateStateParams(processingState,
wrapOutcomeReceiver(callback)));
+ mHandler.sendMessage(msg);
}
};
}
@@ -471,7 +588,7 @@
IResponseCallback callback) {
return new ProcessingCallback() {
@Override
- public void onResult(@androidx.annotation.NonNull Bundle result) {
+ public void onResult(@NonNull Bundle result) {
try {
callback.onSuccess(result);
} catch (RemoteException e) {
@@ -507,7 +624,7 @@
IStreamingResponseCallback callback) {
return new StreamingProcessingCallback() {
@Override
- public void onPartialResult(@androidx.annotation.NonNull Bundle partialResult) {
+ public void onPartialResult(@NonNull Bundle partialResult) {
try {
callback.onNewContent(partialResult);
} catch (RemoteException e) {
@@ -516,7 +633,7 @@
}
@Override
- public void onResult(@androidx.annotation.NonNull Bundle result) {
+ public void onResult(@NonNull Bundle result) {
try {
callback.onSuccess(result);
} catch (RemoteException e) {
@@ -549,7 +666,7 @@
}
private RemoteCallback wrapRemoteCallback(
- @androidx.annotation.NonNull Consumer<Bundle> contentCallback) {
+ @NonNull Consumer<Bundle> contentCallback) {
return new RemoteCallback(
result -> {
if (result != null) {
@@ -604,7 +721,7 @@
@Override
public void onError(
- @androidx.annotation.NonNull OnDeviceIntelligenceException error) {
+ @NonNull OnDeviceIntelligenceException error) {
try {
callback.onFailure(error.getErrorCode(), error.getMessage());
} catch (RemoteException e) {
diff --git a/packages/NeuralNetworks/service/Android.bp b/packages/NeuralNetworks/service/Android.bp
new file mode 100644
index 0000000..05c603f
--- /dev/null
+++ b/packages/NeuralNetworks/service/Android.bp
@@ -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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+filegroup {
+ name: "service-ondeviceintelligence-sources",
+ srcs: [
+ "java/**/*.java",
+ ],
+ path: "java",
+ visibility: [
+ "//frameworks/base:__subpackages__",
+ "//packages/modules/NeuralNetworks:__subpackages__",
+ ],
+}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java b/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/BundleUtil.java
similarity index 94%
rename from services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
rename to packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/BundleUtil.java
index 7dd8f2f..2626cc8 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
+++ b/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/BundleUtil.java
@@ -21,6 +21,7 @@
import static android.system.OsConstants.O_RDONLY;
import static android.system.OsConstants.PROT_READ;
+import android.annotation.SuppressLint;
import android.app.ondeviceintelligence.IResponseCallback;
import android.app.ondeviceintelligence.IStreamingResponseCallback;
import android.app.ondeviceintelligence.ITokenInfoCallback;
@@ -42,7 +43,7 @@
import android.system.Os;
import android.util.Log;
-import com.android.internal.infra.AndroidFuture;
+import com.android.modules.utils.AndroidFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeoutException;
@@ -50,6 +51,8 @@
/**
* Util methods for ensuring the Bundle passed in various methods are read-only and restricted to
* some known types.
+ *
+ * @hide
*/
public class BundleUtil {
private static final String TAG = "BundleUtil";
@@ -76,7 +79,7 @@
* {@link ClassNotFoundException} exception is swallowed and `null` is returned
* instead. We want to ensure cleanup of null entries in such case.
*/
- bundle.putObject(key, null);
+ bundle.putParcelable(key, null);
continue;
}
if (canMarshall(obj) || obj instanceof CursorWindow) {
@@ -122,7 +125,7 @@
* {@link ClassNotFoundException} exception is swallowed and `null` is returned
* instead. We want to ensure cleanup of null entries in such case.
*/
- bundle.putObject(key, null);
+ bundle.putParcelable(key, null);
continue;
}
if (canMarshall(obj)) {
@@ -167,7 +170,7 @@
* {@link ClassNotFoundException} exception is swallowed and `null` is returned
* instead. We want to ensure cleanup of null entries in such case.
*/
- bundle.putObject(key, null);
+ bundle.putParcelable(key, null);
continue;
}
if (canMarshall(obj)) {
@@ -317,11 +320,16 @@
};
}
- private static boolean canMarshall(Object obj) {
- return obj instanceof byte[] || obj instanceof PersistableBundle
- || PersistableBundle.isValidType(obj);
+ private static boolean canMarshall(Object value) {
+ return (value instanceof byte[]) || (value instanceof Integer) || (value instanceof Long) ||
+ (value instanceof Double) || (value instanceof String) ||
+ (value instanceof int[]) || (value instanceof long[]) ||
+ (value instanceof double[]) || (value instanceof String[]) ||
+ (value instanceof PersistableBundle) || (value == null) ||
+ (value instanceof Boolean) || (value instanceof boolean[]);
}
+ @SuppressLint("NewApi")
private static void ensureValidBundle(Bundle bundle) {
if (bundle == null) {
throw new IllegalArgumentException("Request passed is expected to be non-null");
@@ -364,7 +372,7 @@
}
} catch (ErrnoException e) {
throw new BadParcelableException(
- "Invalid File descriptor passed in the Bundle.", e);
+ "Invalid File descriptor passed in the Bundle.");
}
}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java b/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
similarity index 99%
rename from services/core/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
rename to packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
index bef3f80..e8a1b322 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
+++ b/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
@@ -28,6 +28,9 @@
import java.util.List;
import java.util.TreeSet;
+/**
+ * @hide
+ */
public class InferenceInfoStore {
private static final String TAG = "InferenceInfoStore";
private final TreeSet<InferenceInfo> inferenceInfos;
@@ -98,4 +101,4 @@
info.startTimeMs).setEndTimeMillis(info.endTimeMs).setSuspendedTimeMillis(
info.suspendedTimeMs).build();
}
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerInternal.java b/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerLocal.java
similarity index 60%
rename from services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerInternal.java
rename to packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerLocal.java
index 1450dc0..6badc53 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerInternal.java
+++ b/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerLocal.java
@@ -16,7 +16,21 @@
package com.android.server.ondeviceintelligence;
-public interface OnDeviceIntelligenceManagerInternal {
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
+
+/**
+ * Exposes APIs to {@code system_server} components outside of the module boundaries.
+ * <p> This API should be access using {@link com.android.server.LocalManagerRegistry}. </p>
+ *
+ * @hide
+ */
+@SystemApi(client = Client.SYSTEM_SERVER)
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE)
+public interface OnDeviceIntelligenceManagerLocal {
/**
* Gets the uid for the process that is currently hosting the
* {@link android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService} registered on
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
similarity index 84%
rename from services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
rename to packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
index b0d69e6..607ec1c 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
+++ b/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
@@ -16,38 +16,42 @@
package com.android.server.ondeviceintelligence;
+import static android.app.ondeviceintelligence.flags.Flags.enableOnDeviceIntelligenceModule;
+
+import static android.app.ondeviceintelligence.OnDeviceIntelligenceManager.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS;
import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.DEVICE_CONFIG_UPDATE_BUNDLE_KEY;
-import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_LOADED_BUNDLE_KEY;
-import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_UNLOADED_BUNDLE_KEY;
import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_LOADED_BROADCAST_INTENT;
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_LOADED_BUNDLE_KEY;
import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_UNLOADED_BROADCAST_INTENT;
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_UNLOADED_BUNDLE_KEY;
import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.REGISTER_MODEL_UPDATE_CALLBACK_BUNDLE_KEY;
import static com.android.server.ondeviceintelligence.BundleUtil.sanitizeInferenceParams;
-import static com.android.server.ondeviceintelligence.BundleUtil.validatePfdReadOnly;
import static com.android.server.ondeviceintelligence.BundleUtil.sanitizeStateParams;
+import static com.android.server.ondeviceintelligence.BundleUtil.validatePfdReadOnly;
import static com.android.server.ondeviceintelligence.BundleUtil.wrapWithValidation;
-
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.app.AppGlobals;
import android.app.ondeviceintelligence.DownloadCallback;
import android.app.ondeviceintelligence.Feature;
import android.app.ondeviceintelligence.FeatureDetails;
+import android.app.ondeviceintelligence.ICancellationSignal;
import android.app.ondeviceintelligence.IDownloadCallback;
import android.app.ondeviceintelligence.IFeatureCallback;
import android.app.ondeviceintelligence.IFeatureDetailsCallback;
import android.app.ondeviceintelligence.IListFeaturesCallback;
import android.app.ondeviceintelligence.IOnDeviceIntelligenceManager;
import android.app.ondeviceintelligence.IProcessingSignal;
+import android.app.ondeviceintelligence.IRemoteCallback;
import android.app.ondeviceintelligence.IResponseCallback;
import android.app.ondeviceintelligence.IStreamingResponseCallback;
import android.app.ondeviceintelligence.ITokenInfoCallback;
import android.app.ondeviceintelligence.InferenceInfo;
import android.app.ondeviceintelligence.OnDeviceIntelligenceException;
+import android.app.ondeviceintelligence.utils.BinderUtils;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -58,16 +62,12 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.ICancellationSignal;
-import android.os.IRemoteCallback;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.ShellCallback;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings;
@@ -82,17 +82,14 @@
import android.util.Log;
import android.util.Slog;
-import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.infra.AndroidFuture;
-import com.android.internal.infra.ServiceConnector;
-import com.android.internal.os.BackgroundThread;
-import com.android.server.LocalServices;
+import com.android.modules.utils.AndroidFuture;
+import com.android.modules.utils.BackgroundThread;
+import com.android.modules.utils.ServiceConnector;
+import com.android.server.LocalManagerRegistry;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
import com.android.server.ondeviceintelligence.callbacks.ListenableDownloadCallback;
-import java.io.FileDescriptor;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
@@ -182,9 +179,11 @@
public void onStart() {
publishBinderService(
Context.ON_DEVICE_INTELLIGENCE_SERVICE, getOnDeviceIntelligenceManagerService(),
- /* allowIsolated = */true);
- LocalServices.addService(OnDeviceIntelligenceManagerInternal.class,
- this::getRemoteInferenceServiceUid);
+ /* allowIsolated = */ true);
+ if (enableOnDeviceIntelligenceModule()) {
+ LocalManagerRegistry.addManager(OnDeviceIntelligenceManagerLocal.class,
+ this::getRemoteInferenceServiceUid);
+ }
}
@Override
@@ -203,10 +202,10 @@
public void onUserUnlocked(@NonNull TargetUser user) {
Slog.d(TAG, "onUserUnlocked: " + user.getUserHandle());
//connect to remote services(if available) during boot.
- if(user.getUserHandle().equals(UserHandle.SYSTEM)) {
+ if (user.getUserHandle().equals(UserHandle.SYSTEM)) {
try {
- ensureRemoteInferenceServiceInitialized();
- ensureRemoteIntelligenceServiceInitialized();
+ ensureRemoteInferenceServiceInitialized(/* throwServiceIfInvalid */ false);
+ ensureRemoteIntelligenceServiceInitialized(/* throwServiceIfInvalid */ false);
} catch (Exception e) {
Slog.w(TAG, "Couldn't pre-start remote ondeviceintelligence services.", e);
}
@@ -251,7 +250,7 @@
remoteCallback.sendResult(null);
return;
}
- ensureRemoteIntelligenceServiceInitialized();
+ ensureRemoteIntelligenceServiceInitialized(/* throwServiceIfInvalid */ true);
mRemoteOnDeviceIntelligenceService.postAsync(
service -> {
AndroidFuture future = new AndroidFuture();
@@ -279,7 +278,7 @@
PersistableBundle.EMPTY);
return;
}
- ensureRemoteIntelligenceServiceInitialized();
+ ensureRemoteIntelligenceServiceInitialized(/* throwServiceIfInvalid */ true);
int callerUid = Binder.getCallingUid();
mRemoteOnDeviceIntelligenceService.postAsync(
service -> {
@@ -317,7 +316,7 @@
PersistableBundle.EMPTY);
return;
}
- ensureRemoteIntelligenceServiceInitialized();
+ ensureRemoteIntelligenceServiceInitialized(/* throwServiceIfInvalid */ true);
int callerUid = Binder.getCallingUid();
mRemoteOnDeviceIntelligenceService.postAsync(
service -> {
@@ -361,7 +360,7 @@
PersistableBundle.EMPTY);
return;
}
- ensureRemoteIntelligenceServiceInitialized();
+ ensureRemoteIntelligenceServiceInitialized(/* throwServiceIfInvalid */ true);
int callerUid = Binder.getCallingUid();
mRemoteOnDeviceIntelligenceService.postAsync(
service -> {
@@ -404,7 +403,7 @@
"OnDeviceIntelligenceManagerService is unavailable",
PersistableBundle.EMPTY);
}
- ensureRemoteIntelligenceServiceInitialized();
+ ensureRemoteIntelligenceServiceInitialized(/* throwServiceIfInvalid */ true);
int callerUid = Binder.getCallingUid();
mRemoteOnDeviceIntelligenceService.postAsync(
service -> {
@@ -444,7 +443,7 @@
"OnDeviceIntelligenceManagerService is unavailable",
PersistableBundle.EMPTY);
}
- ensureRemoteInferenceServiceInitialized();
+ ensureRemoteInferenceServiceInitialized(/* throwServiceIfInvalid */ true);
int callerUid = Binder.getCallingUid();
result = mRemoteInferenceService.postAsync(
service -> {
@@ -488,7 +487,7 @@
"OnDeviceIntelligenceManagerService is unavailable",
PersistableBundle.EMPTY);
}
- ensureRemoteInferenceServiceInitialized();
+ ensureRemoteInferenceServiceInitialized(/* throwServiceIfInvalid */ true);
int callerUid = Binder.getCallingUid();
result = mRemoteInferenceService.postAsync(
service -> {
@@ -534,7 +533,7 @@
"OnDeviceIntelligenceManagerService is unavailable",
PersistableBundle.EMPTY);
}
- ensureRemoteInferenceServiceInitialized();
+ ensureRemoteInferenceServiceInitialized(/* throwServiceIfInvalid */ true);
int callerUid = Binder.getCallingUid();
result = mRemoteInferenceService.postAsync(
service -> {
@@ -559,20 +558,31 @@
}
@Override
- public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
- new OnDeviceIntelligenceShellCommand(OnDeviceIntelligenceManagerService.this).exec(
- this, in, out, err, args, callback, resultReceiver);
+ public int handleShellCommand(@NonNull ParcelFileDescriptor in,
+ @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
+ @NonNull String[] args) {
+ return new com.android.server.ondeviceintelligence.OnDeviceIntelligenceShellCommand(
+ OnDeviceIntelligenceManagerService.this).exec(
+ this,
+ in.getFileDescriptor(),
+ out.getFileDescriptor(),
+ err.getFileDescriptor(),
+ args);
}
};
}
- private void ensureRemoteIntelligenceServiceInitialized() {
+ private boolean ensureRemoteIntelligenceServiceInitialized(boolean throwIfServiceInvalid) {
synchronized (mLock) {
if (mRemoteOnDeviceIntelligenceService == null) {
String serviceName = getServiceNames()[0];
- Binder.withCleanCallingIdentity(() -> validateServiceElevated(serviceName, false));
- mRemoteOnDeviceIntelligenceService = new RemoteOnDeviceIntelligenceService(mContext,
+ if (!BinderUtils.withCleanCallingIdentity(
+ () -> validateServiceElevated(serviceName, false,
+ throwIfServiceInvalid))) {
+ return false;
+ }
+ mRemoteOnDeviceIntelligenceService = new RemoteOnDeviceIntelligenceService(
+ mContext,
ComponentName.unflattenFromString(serviceName),
UserHandle.SYSTEM.getIdentifier());
mRemoteOnDeviceIntelligenceService.setServiceLifecycleCallbacks(
@@ -591,6 +601,7 @@
});
}
}
+ return true;
}
@NonNull
@@ -604,13 +615,21 @@
AndroidFuture<Void> result = null;
try {
sanitizeStateParams(processingState);
- ensureRemoteInferenceServiceInitialized();
- result = mRemoteInferenceService.post(
- service -> service.updateProcessingState(
- processingState, callback));
- result.whenCompleteAsync(
- (c, e) -> BundleUtil.tryCloseResource(processingState),
- resourceClosingExecutor);
+ if (ensureRemoteInferenceServiceInitialized(/* throwServiceIfInvalid */
+ false)) {
+ result = mRemoteInferenceService.post(
+ service -> service.updateProcessingState(
+ processingState, callback));
+ result.whenCompleteAsync(
+ (c, e) -> BundleUtil.tryCloseResource(processingState),
+ resourceClosingExecutor);
+ } else {
+ callback.onFailure(
+ OnDeviceIntelligenceException.PROCESSING_ERROR_SERVICE_UNAVAILABLE,
+ "Remote service cannot be initialized.");
+ }
+ } catch (RemoteException e) {
+ Slog.w("Failed to invoke updateProcessingState", e);
} finally {
if (result == null) {
resourceClosingExecutor.execute(
@@ -622,11 +641,14 @@
};
}
- private void ensureRemoteInferenceServiceInitialized() {
+ private boolean ensureRemoteInferenceServiceInitialized(boolean throwIfServiceInvalid) {
synchronized (mLock) {
if (mRemoteInferenceService == null) {
String serviceName = getServiceNames()[1];
- Binder.withCleanCallingIdentity(() -> validateServiceElevated(serviceName, true));
+ if (!BinderUtils.withCleanCallingIdentity(
+ () -> validateServiceElevated(serviceName, true, throwIfServiceInvalid))) {
+ return false;
+ }
mRemoteInferenceService = new RemoteOnDeviceSandboxedInferenceService(mContext,
ComponentName.unflattenFromString(serviceName),
UserHandle.SYSTEM.getIdentifier());
@@ -636,7 +658,11 @@
public void onConnected(
@NonNull IOnDeviceSandboxedInferenceService service) {
try {
- ensureRemoteIntelligenceServiceInitialized();
+ if (!ensureRemoteIntelligenceServiceInitialized(
+ /* throwServiceIfInvalid */
+ false)) {
+ return;
+ }
service.registerRemoteStorageService(
getIRemoteStorageService(), new IRemoteCallback.Stub() {
@Override
@@ -659,20 +685,29 @@
@Override
public void onDisconnected(
@NonNull IOnDeviceSandboxedInferenceService service) {
- ensureRemoteIntelligenceServiceInitialized();
+ if (!ensureRemoteIntelligenceServiceInitialized(
+ /* throwServiceIfInvalid */
+ false)) {
+ return;
+ }
mRemoteOnDeviceIntelligenceService.run(
IOnDeviceIntelligenceService::notifyInferenceServiceDisconnected);
}
@Override
public void onBinderDied() {
- ensureRemoteIntelligenceServiceInitialized();
+ if (!ensureRemoteIntelligenceServiceInitialized(
+ /* throwServiceIfInvalid */
+ false)) {
+ return;
+ }
mRemoteOnDeviceIntelligenceService.run(
IOnDeviceIntelligenceService::notifyInferenceServiceDisconnected);
}
});
}
}
+ return true;
}
private void registerModelLoadingBroadcasts(IOnDeviceSandboxedInferenceService service) {
@@ -743,9 +778,13 @@
if (mTemporaryConfigNamespace != null) {
return mTemporaryConfigNamespace;
}
-
- return mContext.getResources().getString(
- R.string.config_defaultOnDeviceIntelligenceDeviceConfigNamespace);
+ return mContext.getResources()
+ .getString(
+ mContext.getResources()
+ .getIdentifier(
+ "config_defaultOnDeviceIntelligenceDeviceConfigNamespace",
+ "string",
+ "android"));
}
}
@@ -759,7 +798,11 @@
}
Bundle bundle = new Bundle();
bundle.putParcelable(DEVICE_CONFIG_UPDATE_BUNDLE_KEY, persistableBundle);
- ensureRemoteInferenceServiceInitialized();
+ if (!ensureRemoteIntelligenceServiceInitialized(
+ /* throwServiceIfInvalid */
+ false)) {
+ return;
+ }
mRemoteInferenceService.run(service -> service.updateProcessingState(bundle,
new IProcessingUpdateStatusCallback.Stub() {
@Override
@@ -782,7 +825,13 @@
public void getReadOnlyFileDescriptor(
String filePath,
AndroidFuture<ParcelFileDescriptor> future) {
- ensureRemoteIntelligenceServiceInitialized();
+ if (!ensureRemoteIntelligenceServiceInitialized(
+ /* throwServiceIfInvalid */
+ false)) {
+ future.completeExceptionally(new OnDeviceIntelligenceException(
+ OnDeviceIntelligenceException.PROCESSING_ERROR_NOT_AVAILABLE));
+ return;
+ }
AndroidFuture<ParcelFileDescriptor> pfdFuture = new AndroidFuture<>();
mRemoteOnDeviceIntelligenceService.run(
service -> service.getReadOnlyFileDescriptor(
@@ -805,7 +854,7 @@
public void getReadOnlyFeatureFileDescriptorMap(
Feature feature,
RemoteCallback remoteCallback) {
- ensureRemoteIntelligenceServiceInitialized();
+ ensureRemoteIntelligenceServiceInitialized(/* throwServiceIfInvalid */ true);
mRemoteOnDeviceIntelligenceService.run(
service -> service.getReadOnlyFeatureFileDescriptorMap(
feature,
@@ -829,40 +878,48 @@
};
}
- private void validateServiceElevated(String serviceName, boolean checkIsolated) {
+ private boolean validateServiceElevated(String serviceName, boolean checkIsolated,
+ boolean throwIfServiceInvalid) {
try {
if (TextUtils.isEmpty(serviceName)) {
- throw new IllegalStateException(
- "Remote service is not configured to complete the request");
+ if (throwIfServiceInvalid) {
+ throw new IllegalStateException(
+ "Remote service is not configured to complete the request");
+ }
+ return false;
}
ComponentName serviceComponent = ComponentName.unflattenFromString(
serviceName);
- ServiceInfo serviceInfo = AppGlobals.getPackageManager().getServiceInfo(
+ ServiceInfo serviceInfo = mContext.getPackageManager().getServiceInfo(
serviceComponent,
PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- UserHandle.SYSTEM.getIdentifier());
- if (serviceInfo != null) {
- if (!checkIsolated) {
- checkServiceRequiresPermission(serviceInfo,
- Manifest.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE);
- return;
- }
-
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+ if (!checkIsolated) {
checkServiceRequiresPermission(serviceInfo,
- Manifest.permission.BIND_ON_DEVICE_SANDBOXED_INFERENCE_SERVICE);
- if (!isIsolatedService(serviceInfo)) {
- throw new SecurityException(
- "Call required an isolated service, but the configured service: "
- + serviceName + ", is not isolated");
- }
- } else {
+ Manifest.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE);
+ return true;
+ }
+
+ checkServiceRequiresPermission(serviceInfo,
+ Manifest.permission.BIND_ON_DEVICE_SANDBOXED_INFERENCE_SERVICE);
+ if (!isIsolatedService(serviceInfo)) {
+ throw new SecurityException(
+ "Call required an isolated service, but the configured service: "
+ + serviceName + ", is not isolated");
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ if (throwIfServiceInvalid) {
throw new IllegalStateException(
"Remote service is not configured to complete the request.");
}
- } catch (RemoteException e) {
- throw new IllegalStateException("Could not fetch service info for remote services", e);
+ return false;
+ } catch (SecurityException e) {
+ if (throwIfServiceInvalid) {
+ throw e;
+ }
+ return false;
}
+ return true;
}
private static void checkServiceRequiresPermission(ServiceInfo serviceInfo,
@@ -870,8 +927,8 @@
final String permission = serviceInfo.permission;
if (!requiredPermission.equals(permission)) {
throw new SecurityException(String.format(
- "Service %s requires %s permission. Found %s permission",
- serviceInfo.getComponentName(),
+ "%s requires %s permission. Found %s permission",
+ serviceInfo,
requiredPermission,
serviceInfo.permission));
}
@@ -909,10 +966,22 @@
return mTemporaryServiceNames;
}
}
- return new String[]{mContext.getResources().getString(
- R.string.config_defaultOnDeviceIntelligenceService),
- mContext.getResources().getString(
- R.string.config_defaultOnDeviceSandboxedInferenceService)};
+ return new String[]{
+ mContext.getResources()
+ .getString(
+ mContext.getResources()
+ .getIdentifier(
+ "config_defaultOnDeviceIntelligenceService",
+ "string",
+ "android")),
+ mContext.getResources()
+ .getString(
+ mContext.getResources()
+ .getIdentifier(
+ "config_defaultOnDeviceSandboxedInferenceService",
+ "string",
+ "android"))
+ };
}
protected String[] getBroadcastKeys() throws Resources.NotFoundException {
@@ -923,7 +992,7 @@
}
}
- return new String[]{ MODEL_LOADED_BROADCAST_INTENT, MODEL_UNLOADED_BROADCAST_INTENT };
+ return new String[]{MODEL_LOADED_BROADCAST_INTENT, MODEL_UNLOADED_BROADCAST_INTENT};
}
@RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
@@ -1068,7 +1137,7 @@
private synchronized Handler getTemporaryHandler() {
if (mTemporaryHandler == null) {
- mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
+ mTemporaryHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
synchronized (mLock) {
@@ -1090,10 +1159,13 @@
return mTemporaryHandler;
}
+ // Using #getLong here as the timeout settings are only applicable to the services running in
+ // SYSTEM user only.
+ @SuppressWarnings("NonUserGetterCalled")
private long getIdleTimeoutMs() {
- return Settings.Secure.getLongForUser(mContext.getContentResolver(),
- Settings.Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS, TimeUnit.HOURS.toMillis(1),
- mContext.getUserId());
+ return Settings.Secure.getLong(mContext.getContentResolver(),
+ ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS,
+ TimeUnit.HOURS.toMillis(1));
}
private int getRemoteInferenceServiceUid() {
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java b/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
similarity index 97%
rename from services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
rename to packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
index d2c84fa..c641de8 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
+++ b/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
@@ -18,12 +18,16 @@
import android.annotation.NonNull;
import android.os.Binder;
-import android.os.ShellCommand;
+
+import com.android.modules.utils.BasicShellCommandHandler;
import java.io.PrintWriter;
import java.util.Objects;
-final class OnDeviceIntelligenceShellCommand extends ShellCommand {
+/**
+ * @hide
+ */
+final class OnDeviceIntelligenceShellCommand extends BasicShellCommandHandler {
private static final String TAG = OnDeviceIntelligenceShellCommand.class.getSimpleName();
@NonNull
diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java b/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
similarity index 80%
rename from services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
rename to packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
index ac9747a..0c43a30 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
+++ b/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
@@ -16,6 +16,7 @@
package com.android.server.ondeviceintelligence;
+import static android.app.ondeviceintelligence.OnDeviceIntelligenceManager.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
@@ -26,13 +27,15 @@
import android.service.ondeviceintelligence.IOnDeviceIntelligenceService;
import android.service.ondeviceintelligence.OnDeviceIntelligenceService;
-import com.android.internal.infra.ServiceConnector;
+import com.android.modules.utils.ServiceConnector;
import java.util.concurrent.TimeUnit;
/**
* Manages the connection to the remote on-device intelligence service. Also, handles unbinding
* logic set by the service implementation via a Secure Settings flag.
+ *
+ * @hide
*/
public class RemoteOnDeviceIntelligenceService extends
ServiceConnector.Impl<IOnDeviceIntelligenceService> {
@@ -56,11 +59,13 @@
return LONG_TIMEOUT;
}
+ // Using #getLong here as the timeout settings are only applicable to the services running in
+ // SYSTEM user only.
@Override
+ @SuppressWarnings("NonUserGetterCalled")
protected long getAutoDisconnectTimeoutMs() {
- return Settings.Secure.getLongForUser(mContext.getContentResolver(),
- Settings.Secure.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS,
- TimeUnit.SECONDS.toMillis(30),
- mContext.getUserId());
+ return Settings.Secure.getLong(mContext.getContentResolver(),
+ ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS,
+ TimeUnit.SECONDS.toMillis(30));
}
}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java b/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
similarity index 82%
rename from services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
rename to packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
index 18b1383..8c5d5a7 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
+++ b/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
@@ -16,6 +16,7 @@
package com.android.server.ondeviceintelligence;
+import static android.app.ondeviceintelligence.OnDeviceIntelligenceManager.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
@@ -26,7 +27,7 @@
import android.service.ondeviceintelligence.IOnDeviceSandboxedInferenceService;
import android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService;
-import com.android.internal.infra.ServiceConnector;
+import com.android.modules.utils.ServiceConnector;
import java.util.concurrent.TimeUnit;
@@ -35,6 +36,8 @@
* Manages the connection to the remote on-device sand boxed inference service. Also, handles
* unbinding
* logic set by the service implementation via a SecureSettings flag.
+ *
+ * @hide
*/
public class RemoteOnDeviceSandboxedInferenceService extends
ServiceConnector.Impl<IOnDeviceSandboxedInferenceService> {
@@ -65,12 +68,13 @@
return LONG_TIMEOUT;
}
-
+ // Using #getLong here as the timeout settings are only applicable to the services running in
+ // SYSTEM user only.
@Override
+ @SuppressWarnings("NonUserGetterCalled")
protected long getAutoDisconnectTimeoutMs() {
- return Settings.Secure.getLongForUser(mContext.getContentResolver(),
- Settings.Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS,
- TimeUnit.SECONDS.toMillis(30),
- mContext.getUserId());
+ return Settings.Secure.getLong(mContext.getContentResolver(),
+ ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS,
+ TimeUnit.SECONDS.toMillis(30));
}
}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java b/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
similarity index 98%
rename from services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
rename to packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
index 32f0698..249bcd3 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
+++ b/packages/NeuralNetworks/service/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
@@ -21,7 +21,7 @@
import android.os.PersistableBundle;
import android.os.RemoteException;
-import com.android.internal.infra.AndroidFuture;
+import com.android.modules.utils.AndroidFuture;
import java.util.concurrent.TimeoutException;
@@ -32,6 +32,8 @@
* some cases. Instead, in such cases we rely on the remote service sending progress updates and if
* there are *no* progress callbacks in the duration of {@link #idleTimeoutMs}, we can assume the
* download will not complete and enabling faster cleanup.
+ *
+ * @hide
*/
public class ListenableDownloadCallback extends IDownloadCallback.Stub implements Runnable {
private final IDownloadCallback callback;
diff --git a/packages/PackageInstaller/TEST_MAPPING b/packages/PackageInstaller/TEST_MAPPING
index 50db501..5033101 100644
--- a/packages/PackageInstaller/TEST_MAPPING
+++ b/packages/PackageInstaller/TEST_MAPPING
@@ -45,6 +45,17 @@
]
},
{
+ "name": "CtsPackageInstallerCUJUpdateOwnerShipTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJUpdateSelfTestCases",
"options":[
{
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
index cd03dd7..07b1c9e 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
@@ -22,6 +22,7 @@
import com.google.errorprone.annotations.CanIgnoreReturnValue
import java.util.WeakHashMap
import java.util.concurrent.Executor
+import java.util.concurrent.atomic.AtomicInteger
/**
* Callback to be informed of changes in [KeyedObservable] object.
@@ -203,13 +204,71 @@
}
}
- fun hasAnyObserver(): Boolean {
+ open fun hasAnyObserver(): Boolean {
synchronized(observers) { if (observers.isNotEmpty()) return true }
synchronized(keyedObservers) { if (keyedObservers.isNotEmpty()) return true }
return false
}
}
+/** [KeyedDataObservable] that maintains a counter for the observers. */
+abstract class AbstractKeyedDataObservable<K> : KeyedDataObservable<K>() {
+ /**
+ * Counter of observers.
+ *
+ * The value is accurate only when [addObserver] and [removeObserver] are invoked in pairs.
+ */
+ private val counter = AtomicInteger()
+
+ override fun addObserver(observer: KeyedObserver<K?>, executor: Executor) =
+ if (super.addObserver(observer, executor)) {
+ onObserverAdded()
+ true
+ } else {
+ false
+ }
+
+ override fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor) =
+ if (super.addObserver(key, observer, executor)) {
+ onObserverAdded()
+ true
+ } else {
+ false
+ }
+
+ private fun onObserverAdded() {
+ if (counter.getAndIncrement() == 0) onFirstObserverAdded()
+ }
+
+ /** Callbacks when the first observer is just added. */
+ protected abstract fun onFirstObserverAdded()
+
+ override fun removeObserver(observer: KeyedObserver<K?>) =
+ if (super.removeObserver(observer)) {
+ onObserverRemoved()
+ true
+ } else {
+ false
+ }
+
+ override fun removeObserver(key: K, observer: KeyedObserver<K>) =
+ if (super.removeObserver(key, observer)) {
+ onObserverRemoved()
+ true
+ } else {
+ false
+ }
+
+ private fun onObserverRemoved() {
+ if (counter.decrementAndGet() == 0) onLastObserverRemoved()
+ }
+
+ /** Callbacks when the last observer is just removed. */
+ protected abstract fun onLastObserverRemoved()
+
+ override fun hasAnyObserver() = counter.get() > 0
+}
+
/** [KeyedObservable] with no-op implementations for all interfaces. */
open class NoOpKeyedObservable<K> : KeyedObservable<K> {
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
index 04d4bfe..d6e7a89 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
@@ -20,21 +20,10 @@
import android.database.ContentObserver
import android.net.Uri
import android.util.Log
-import java.util.concurrent.Executor
-import java.util.concurrent.atomic.AtomicInteger
/** Base class of the Settings provider data stores. */
abstract class SettingsStore(protected val contentResolver: ContentResolver) :
- KeyedDataObservable<String>(), KeyValueStore {
-
- /**
- * Counter of observers.
- *
- * The value is accurate only when [addObserver] and [removeObserver] are called correctly. When
- * an observer is not removed (and its weak reference is garbage collected), the content
- * observer is not unregistered but this is not a big deal.
- */
- private val counter = AtomicInteger()
+ AbstractKeyedDataObservable<String>(), KeyValueStore {
private val contentObserver =
object : ContentObserver(HandlerExecutor.main) {
@@ -48,49 +37,15 @@
}
}
- override fun addObserver(observer: KeyedObserver<String?>, executor: Executor) =
- if (super.addObserver(observer, executor)) {
- onObserverAdded()
- true
- } else {
- false
- }
+ /** The URI to watch for any key change. */
+ protected abstract val uri: Uri
- override fun addObserver(key: String, observer: KeyedObserver<String>, executor: Executor) =
- if (super.addObserver(key, observer, executor)) {
- onObserverAdded()
- true
- } else {
- false
- }
-
- private fun onObserverAdded() {
- if (counter.getAndIncrement() != 0) return
+ override fun onFirstObserverAdded() {
Log.i(tag, "registerContentObserver")
contentResolver.registerContentObserver(uri, true, contentObserver)
}
- /** The URI to watch for any key change. */
- protected abstract val uri: Uri
-
- override fun removeObserver(observer: KeyedObserver<String?>) =
- if (super.removeObserver(observer)) {
- onObserverRemoved()
- true
- } else {
- false
- }
-
- override fun removeObserver(key: String, observer: KeyedObserver<String>) =
- if (super.removeObserver(key, observer)) {
- onObserverRemoved()
- true
- } else {
- false
- }
-
- private fun onObserverRemoved() {
- if (counter.decrementAndGet() != 0) return
+ override fun onLastObserverRemoved() {
Log.i(tag, "unregisterContentObserver")
contentResolver.unregisterContentObserver(contentObserver)
}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt
index 7436ac1..6704ecc 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt
@@ -20,6 +20,8 @@
import android.content.ContextWrapper
import android.content.Intent
import android.os.Bundle
+import androidx.lifecycle.LifecycleCoroutineScope
+import kotlinx.coroutines.CoroutineScope
/**
* Interface to provide dynamic preference title.
@@ -138,6 +140,13 @@
*/
abstract class PreferenceLifecycleContext(context: Context) : ContextWrapper(context) {
+ /**
+ * [CoroutineScope] tied to the lifecycle, which is cancelled when the lifecycle is destroyed.
+ *
+ * @see [androidx.lifecycle.lifecycleScope]
+ */
+ abstract val lifecycleScope: LifecycleCoroutineScope
+
/** Returns the preference widget object associated with given key. */
abstract fun <T> findPreference(key: String): T?
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
index 03b225e..6fc9357 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
@@ -19,6 +19,8 @@
import android.content.Context
import android.content.Intent
import android.os.Bundle
+import androidx.lifecycle.LifecycleCoroutineScope
+import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import androidx.preference.PreferenceDataStore
import androidx.preference.PreferenceGroup
@@ -57,6 +59,9 @@
private val preferenceLifecycleContext =
object : PreferenceLifecycleContext(context) {
+ override val lifecycleScope: LifecycleCoroutineScope
+ get() = fragment.lifecycleScope
+
override fun <T> findPreference(key: String) =
preferenceScreen.findPreference(key) as T?
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-af/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-af/strings.xml
index 4bd1d45..b41ec95 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-af/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-af/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Geaktiveer deur administrateur"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Gedeaktiveer deur administrateur"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Geaktiveer deur Gevorderde Beskerming"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Gedeaktiveer deur Gevorderde Beskerming"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-am/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-am/strings.xml
index ef380fd..8e94884 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-am/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-am/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"በላቀ ጥበቃ የነቃ"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"በላቀ ጥበቃ የተሰናከለ"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml
index 57cf30f..8b2ccdf 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"تم التفعيل من خلال ميزة \"الحماية المتقدّمة\""</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"تم الإيقاف من خلال ميزة \"الحماية المتقدّمة\""</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-as/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-as/strings.xml
index 21fe73a..03e9e82 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-as/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-as/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"সুৰক্ষা সম্পৰ্কীয় উন্নত সুবিধাটোৱে সক্ষম কৰিছে"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"সুৰক্ষা সম্পৰ্কীয় উন্নত সুবিধাটোৱে অক্ষম কৰিছে"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-az/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-az/strings.xml
index c7c4c38..9844716 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-az/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-az/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Admin tərəfindən aktiv edildi"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Admin tərəfindən deaktiv edildi"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Qabaqcıl Qoruma tərəfindən aktiv edilib"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Qabaqcıl Qoruma tərəfindən deaktiv edilib"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-b+sr+Latn/strings.xml
index a7d1395..c7b9be2 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-b+sr+Latn/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Administrator je omogućio"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Administrator je onemogućio"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Omogućila je Napredna zaštita"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Onemogućila je Napredna zaštita"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-be/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-be/strings.xml
index 60b9fc6..92ed111 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-be/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-be/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Уключана Палепшанай абаронай"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Адключана Палепшанай абаронай"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-bg/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-bg/strings.xml
index f98a8d4..57b50c5 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-bg/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-bg/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Активирано от „Разширена защита“"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Деактивирано от „Разширена защита“"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-bn/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-bn/strings.xml
index 87e8bd2..939ceb8 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-bn/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-bn/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"উন্নত সুরক্ষা চালু করেছে"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"উন্নত সুরক্ষা বন্ধ করেছে"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-bs/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-bs/strings.xml
index a56917f..87cd3b8 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-bs/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-bs/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Omogućio administrator"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Onemogućio administrator"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Omogućeno je Naprednom zaštitom"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Onemogućeno je Naprednom zaštitom"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ca/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ca/strings.xml
index ac38bd1..34099b6 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ca/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ca/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Activat per l\'administrador"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Desactivat per l\'administrador"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Activat per la Protecció avançada"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Desactivat per la Protecció avançada"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-cs/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-cs/strings.xml
index 1840ecd..82cd56f 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-cs/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-cs/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Zapnuto administrátorem"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Vypnuto administrátorem"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aktivováno pokročilou ochranou"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Deaktivováno pokročilou ochranou"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-da/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-da/strings.xml
index 2e6dd0f..7f7ae8b 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-da/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-da/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Aktiveret af administratoren"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Deaktiveret af administrator"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aktiveret af Avanceret beskyttelse"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Deaktiveret af Avanceret beskyttelse"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-de/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-de/strings.xml
index 88c146c..efaa50e 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-de/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-de/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Vom Administrator aktiviert"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Vom Administrator deaktiviert"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Vom erweiterten Sicherheitsprogramm aktiviert"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Vom erweiterten Sicherheitsprogramm deaktiviert"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-el/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-el/strings.xml
index 941d4ba..ddde3ece 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-el/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-el/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Ενεργοποιήθηκε από την Ενισχυμένη προστασία"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Απενεργοποιήθηκε από την Ενισχυμένη προστασία"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rAU/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rAU/strings.xml
index 91268ec..6a07741 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rAU/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Enabled by admin"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Disabled by admin"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Enabled by Advanced Protection"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Disabled by Advanced Protection"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rCA/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rCA/strings.xml
index 91268ec..6a07741 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rCA/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Enabled by admin"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Disabled by admin"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Enabled by Advanced Protection"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Disabled by Advanced Protection"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rGB/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rGB/strings.xml
index 91268ec..6a07741 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rGB/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Enabled by admin"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Disabled by admin"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Enabled by Advanced Protection"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Disabled by Advanced Protection"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rIN/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rIN/strings.xml
index 91268ec..6a07741 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rIN/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Enabled by admin"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Disabled by admin"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Enabled by Advanced Protection"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Disabled by Advanced Protection"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-es-rUS/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-es-rUS/strings.xml
index 9ea88df..8dc15f7 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-es-rUS/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"El administrador habilitó la opción"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"El administrador inhabilitó la opción"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Habilitado por la Protección avanzada"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Inhabilitado por la Protección avanzada"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-es/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-es/strings.xml
index 35777a2..7c9864d 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-es/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-es/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Habilitado por el administrador"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Inhabilitado por el administrador"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Habilitado por Protección Avanzada"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Inhabilitado por Protección Avanzada"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-et/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-et/strings.xml
index 806d4b8..5939b58 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-et/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-et/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Administraatori lubatud"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Administraatori keelatud"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Lubatud täiustatud kaitsega"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Keelatud täiustatud kaitsega"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-eu/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-eu/strings.xml
index 4fe9b23..27bef6e 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-eu/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-eu/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Administratzaileak gaitu egin du"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Administratzaileak desgaitu du"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Babes aurreratua programak gaitu du"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Babes aurreratua programak desgaitu du"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-fa/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-fa/strings.xml
index 774cb9f..8fb2646 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-fa/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-fa/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"فعالشده با «محافظت پیشرفته»"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"غیرفعالشده با «محافظت پیشرفته»"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-fi/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-fi/strings.xml
index c15882b..cd7cbd3 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-fi/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-fi/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Järjestelmänvalvojan sallima"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Järjestelmänvalvojan estämä"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Lisäsuojaus on ottanut asetuksen käyttöön"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Lisäsuojaus on poistanut asetuksen käytöstä"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-fr-rCA/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-fr-rCA/strings.xml
index 188e9cb..8f0d572 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-fr-rCA/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Activé par l\'administrateur"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Désactivé par l\'administrateur"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Activé par la protection avancée"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Désactivé par la protection avancée"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-fr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-fr/strings.xml
index 188e9cb..2215af2 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-fr/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-fr/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Activé par l\'administrateur"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Désactivé par l\'administrateur"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Activé par la Protection Avancée"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Désactivé par la Protection Avancée"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-gl/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-gl/strings.xml
index de603d0..92f33bb 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-gl/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-gl/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Opción activada polo administrador"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Opción desactivada polo administrador"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Opción activada por Protección avanzada"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Opción desactivada por Protección avanzada"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml
index 3ea8c49..026bdb1 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"અદ્યતન સુરક્ષા દ્વારા ચાલુ કરવામાં આવી છે"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"અદ્યતન સુરક્ષા દ્વારા બંધ કરવામાં આવી છે"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml
index 93ac326..8fc8fd0 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"\'ऐडवांस सुरक्षा\' सेटिंग ने चालू किया है"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"\'ऐडवांस सुरक्षा\' सेटिंग ने बंद किया है"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-hr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-hr/strings.xml
index a56917f..40605a3b 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-hr/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-hr/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Omogućio administrator"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Onemogućio administrator"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Omogućila je napredna zaštita"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Onemogućila je napredna zaštita"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-hu/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-hu/strings.xml
index 897729d..59135a4 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-hu/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-hu/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"A rendszergazda bekapcsolta"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"A rendszergazda letiltotta"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Engedélyezte a Speciális védelem"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Letiltotta a Speciális védelem"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-hy/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-hy/strings.xml
index 7c8e349..0221f93 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-hy/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-hy/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Միացվել է Լրացուցիչ պաշտպանության կողմից"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Անջատվել է Լրացուցիչ պաշտպանության կողմից"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-in/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-in/strings.xml
index 0b08fdb..6beb4c9 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-in/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-in/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Diaktifkan oleh admin"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Dinonaktifkan oleh admin"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Diaktifkan oleh Perlindungan Lanjutan"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Dinonaktifkan oleh Perlindungan Lanjutan"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-is/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-is/strings.xml
index 6a041c7..feb325b 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-is/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-is/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Gert virkt af kerfisstjóra"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Gert óvirkt af kerfisstjóra"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Virkjað af ítarlegri vernd"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Gert óvirkt af ítarlegri vernd"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml
index ee78ae8f..6163920 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Attivata dall\'amministratore"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Opzione disattivata dall\'amministratore"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Attivata dalla protezione avanzata"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Disattivata dalla protezione avanzata"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-iw/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-iw/strings.xml
index c243318..c342041 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-iw/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-iw/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"ההעדפה הופעלה על ידי ההגנה המתקדמת"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"ההעדפה הושבתה על ידי ההגנה המתקדמת"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml
index e4aa5c9..bd386f5 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"高度な保護機能により有効になっています"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"高度な保護機能により無効になっています"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ka/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ka/strings.xml
index 86248f6..a6fde90 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ka/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ka/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"ჩართულია დამატებითი დაცვის საშუალებით"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"გათიშულია დამატებითი დაცვის საშუალებით"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-kk/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-kk/strings.xml
index b945c07..ed0f95c2 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-kk/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-kk/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Күшейтілген қорғаныс параметрі қосып қойды."</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Күшейтілген қорғаныс параметрі өшіріп тастады."</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-km/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-km/strings.xml
index 9deadfd..f2f5ab8 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-km/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-km/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"បានបើកដោយការការពារកម្រិតខ្ពស់"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"បានបិទដោយការការពារកម្រិតខ្ពស់"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-kn/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-kn/strings.xml
index 2af7fe2..ebc41a52 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-kn/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-kn/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"ಸುಧಾರಿತ ಸಂರಕ್ಷಣೆ ಮೂಲಕ ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"ಸುಧಾರಿತ ಸಂರಕ್ಷಣೆ ಮೂಲಕ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ko/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ko/strings.xml
index 62b781f..552662b 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ko/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ko/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"고급 보호 기능으로 사용 설정됨"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"고급 보호 기능으로 사용 중지됨"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ky/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ky/strings.xml
index 8437e9e..375ea19f 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ky/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ky/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Өркүндөтүлгөн коргоо тарабынан иштетилди"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Өркүндөтүлгөн коргоо тарабынан өчүрүлдү"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-lo/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-lo/strings.xml
index be21bf4..4b311c0 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-lo/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-lo/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"ໄດ້ເປີດການນຳໃຊ້ໂດຍການປົກປ້ອງຂັ້ນສູງ"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"ໄດ້ປິດການນຳໃຊ້ໂດຍການປົກປ້ອງຂັ້ນສູງ"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-lt/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-lt/strings.xml
index b290a54..cbbe923 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-lt/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-lt/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Įgalino administratorius"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Išjungė administratorius"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Įgalino Papildoma apsauga"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Išjungė Papildoma apsauga"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-lv/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-lv/strings.xml
index 5f86f98..a5189aa 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-lv/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-lv/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Iespējoja administrators"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Atspējoja administrators"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Iespējota iestatījuma “Papildu aizsardzība” dēļ"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Atspējota iestatījuma “Papildu aizsardzība” dēļ"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-mk/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-mk/strings.xml
index e3f0e75..993b4ae 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-mk/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-mk/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Овозможено од „Напредна заштита“"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Оневозможено од „Напредна заштита“"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ml/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ml/strings.xml
index fe1a4ae..9deeb6a 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ml/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ml/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"വിപുലമായ പരിരക്ഷ പ്രവർത്തനക്ഷമമാക്കി"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"വിപുലമായ പരിരക്ഷ പ്രവർത്തനരഹിതമാക്കി"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-mn/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-mn/strings.xml
index c3799c3..c9a91de 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-mn/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-mn/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Дэвшилтэт хамгаалалтаар идэвхжүүлсэн"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Дэвшилтэт хамгаалалтаар идэвхгүй болгосон"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml
index 4e4660b..ede1242 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"प्रगत संरक्षणाद्वारे सुरू केले आहे"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"प्रगत संरक्षणाद्वारे बंद केले आहे"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ms/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ms/strings.xml
index 4bfcf2e..e8f710a 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ms/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ms/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Didayakan oleh pentadbir"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Dilumpuhkan oleh pentadbir"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Didayakan oleh Perlindungan Lanjutan"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Dilumpuhkan oleh Perlindungan Lanjutan"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-my/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-my/strings.xml
index ef9ab1a9..97be99f 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-my/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-my/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"အဆင့်မြင့်ကာကွယ်ရေးက ဖွင့်ထားသည်"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"အဆင့်မြင့်ကာကွယ်ရေးက ပိတ်ထားသည်"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-nb/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-nb/strings.xml
index 423d7ce..971d1cc 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-nb/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-nb/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Aktivert av administratoren"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Deaktivert av administratoren"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aktivert av Avansert beskyttelse"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Deaktivert av Avansert beskyttelse"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ne/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ne/strings.xml
index 8793a81..3d2a74e 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ne/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ne/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"सुरक्षासम्बन्धी उन्नत सुविधाले अन गरेको छ"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"सुरक्षासम्बन्धी उन्नत सुविधाले अफ गरेको छ"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-nl/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-nl/strings.xml
index 60bb021..9830363 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-nl/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-nl/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Aangezet door beheerder"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Uitgezet door beheerder"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aangezet door Geavanceerde beveiliging"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Uitgezet door Geavanceerde beveiliging"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-or/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-or/strings.xml
index 92004fb..2dab159 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-or/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-or/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"ଆଡଭାନ୍ସଡ ପ୍ରୋଟେକସନ ଦ୍ୱାରା ସକ୍ଷମ କରାଯାଇଛି"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"ଆଡଭାନ୍ସଡ ପ୍ରୋଟେକସନ ଦ୍ୱାରା ଅକ୍ଷମ କରାଯାଇଛି"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pa/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pa/strings.xml
index 144dd4a..12f296a 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-pa/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pa/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"ਅਡਵਾਂਸ ਸੁਰੱਖਿਆ ਵੱਲੋਂ ਚਾਲੂ ਕੀਤੀ ਗਈ"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"ਅਡਵਾਂਸ ਸੁਰੱਖਿਆ ਵੱਲੋਂ ਬੰਦ ਕੀਤੀ ਗਈ"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pl/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pl/strings.xml
index 13814e1..df73947 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-pl/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pl/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Włączone przez administratora"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Wyłączone przez administratora"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Włączone przez Ochronę zaawansowaną"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Wyłączone przez Ochronę zaawansowaną"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rBR/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rBR/strings.xml
index 8e97ffc..a705ba4 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rBR/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Ativado pelo administrador"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Desativado pelo administrador"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Preferência ativada pela Proteção Avançada"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Preferência desativada pela Proteção Avançada"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml
index 8e97ffc..b011183 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Ativado pelo administrador"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Desativado pelo administrador"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Ativado pela Proteção avançada"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Desativado pela Proteção avançada"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pt/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pt/strings.xml
index 8e97ffc..a705ba4 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-pt/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pt/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Ativado pelo administrador"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Desativado pelo administrador"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Preferência ativada pela Proteção Avançada"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Preferência desativada pela Proteção Avançada"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ro/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ro/strings.xml
index 6120ef6..3eb347a 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ro/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ro/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Activat de administrator"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Dezactivat de administrator"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Activată de Protecția avansată"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Dezactivată de Protecția avansată"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ru/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ru/strings.xml
index f06aa06..a004a1f 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ru/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ru/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Включено Дополнительной защитой"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Отключено Дополнительной защитой"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-si/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-si/strings.xml
index c9a9e23..addc8b3 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-si/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-si/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"උසස් ආරක්ෂණය මගින් සබල කර ඇත"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"උසස් ආරක්ෂණය මගින් අබල කර ඇත"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sk/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sk/strings.xml
index f6c1b66..0277696 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-sk/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sk/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Povolené správcom"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Zakázané správcom"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aktivované rozšírenou ochranou"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Deaktivované rozšírenou ochranou"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sl/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sl/strings.xml
index 029cdef..eb886bc 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-sl/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sl/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Omogočil skrbnik"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Onemogočil skrbnik"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Omogočila dodatna zaščita"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Onemogočila dodatna zaščita"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sq/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sq/strings.xml
index 341d6bb..2de5ffe 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-sq/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sq/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Aktivizuar nga administratori"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Çaktivizuar nga administratori"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aktivizuar nga \"Mbrojtja e përparuar\""</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Çaktivizuar nga \"Mbrojtja e përparuar\""</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sr/strings.xml
index a783a02..94f52a0 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-sr/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sr/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Омогућила је Напредна заштита"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Онемогућила је Напредна заштита"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sv/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sv/strings.xml
index c930d47..b41c4d8 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-sv/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sv/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Aktiverad av administratör"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Inaktiverad av administratören"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aktiverades av Avancerat skydd"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Inaktiverades av Avancerat skydd"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sw/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sw/strings.xml
index d61c8fb..4d2e0d9 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-sw/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sw/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Imewashwa na msimamizi"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Imezimwa na msimamizi"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Imewashwa kwa kutumia Ulinzi wa Hali ya Juu"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Imezimwa kwa kutumia Ulinzi wa Hali ya Juu"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ta/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ta/strings.xml
index b39f76f..55b3006 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ta/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ta/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"மேம்பட்ட பாதுகாப்பு அமைப்பால் இயக்கப்பட்டது"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"மேம்பட்ட பாதுகாப்பு அமைப்பால் முடக்கப்பட்டது"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-te/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-te/strings.xml
index 4b8cd74e..fc6d00b 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-te/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-te/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"అడ్వాన్స్డ్ ప్రొటెక్షన్ ద్వారా ఎనేబుల్ చేయబడింది"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"అడ్వాన్స్డ్ ప్రొటెక్షన్ ద్వారా డిజేబుల్ చేయబడింది"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-th/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-th/strings.xml
index 414d3c1..51da8de 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-th/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-th/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"เปิดใช้โดยการปกป้องขั้นสูง"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"ปิดใช้โดยการปกป้องขั้นสูง"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-tl/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-tl/strings.xml
index 34a057d..7fbf0e7 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-tl/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-tl/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Na-enable ng admin"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Na-disable ng admin"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Na-enable ng Advanced na Proteksyon"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Na-disable ng Advanced na Proteksyon"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-tr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-tr/strings.xml
index f8d1f55..a529ca5 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-tr/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-tr/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Yönetici tarafından etkinleştirildi"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Yönetici tarafından devre dışı bırakıldı"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Gelişmiş Koruma tarafından etkinleştirildi"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Gelişmiş Koruma tarafından devre dışı bırakıldı"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-uk/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-uk/strings.xml
index 2fa4634..fdf4160 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-uk/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-uk/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Увімкнено Додатковим захистом"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Вимкнено Додатковим захистом"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ur/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ur/strings.xml
index 9401309..b40cb682 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ur/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ur/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"اعلی تحفظ نے فعال کیا ہے"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"اعلی تحفظ نے غیر فعال کیا ہے"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-uz/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-uz/strings.xml
index a726e6c..9a27735 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-uz/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-uz/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Administrator tomonidan yoqilgan"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Administrator tomonidan faolsizlantirilgan"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Kuchaytirilgan himoya tomonidan yoqilgan"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Kuchaytirilgan himoya tomonidan faolsizlantirilgan"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-vi/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-vi/strings.xml
index 63bb27a..3436762 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-vi/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-vi/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Do quản trị viên bật"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Quản trị viên đã vô hiệu hóa chế độ này"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Được bật bởi chế độ Bảo vệ nâng cao"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Bị tắt bởi chế độ Bảo vệ nâng cao"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rCN/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rCN/strings.xml
index 5698cf3..5c9e302 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rCN/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"已被“高级保护”功能启用"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"已被“高级保护”功能停用"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rHK/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rHK/strings.xml
index f37ccd8..d4b8833 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rHK/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"已由進階保護功能啟用"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"已由進階保護功能停用"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rTW/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rTW/strings.xml
index f37ccd8..d4b8833 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rTW/strings.xml
@@ -19,8 +19,6 @@
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>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"已由進階保護功能啟用"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"已由進階保護功能停用"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-zu/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-zu/strings.xml
index 8489925..2a93d00 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-zu/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-zu/strings.xml
@@ -19,8 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Kunikwe amandla umlawuli"</string>
<string name="disabled_by_admin" msgid="4023569940620832713">"Kukhutshazwe umlawuli"</string>
- <!-- no translation found for enabled_by_advanced_protection (6236917660829422499) -->
- <skip />
- <!-- no translation found for disabled_by_advanced_protection (369596009193239632) -->
- <skip />
+ <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Kunikwe Amandla Ukuvikela Okuthuthukile"</string>
+ <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Kukhutshazwe Ukuvikela Okuthuthukile"</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
index d89d397..5a524d9 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
@@ -133,7 +133,7 @@
list.joinToString(separator = System.lineSeparator())
}
if (footer.isBlank()) return
- HorizontalDivider()
+ if (!isSpaExpressiveEnabled) HorizontalDivider()
Column(
modifier =
if (isSpaExpressiveEnabled) Modifier.padding(SettingsDimension.footerItemPadding)
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index c1f254a..1a043d5 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -187,3 +187,13 @@
description: "Enable the connection status report for a set of hearing device."
bug: "357882387"
}
+
+flag {
+ name: "ignore_a2dp_disconnection_for_android_auto"
+ namespace: "cross_device_experiences"
+ description: "Do not show problem connecting message when Android Auto disconnect A2DP"
+ bug: "381981752"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume.xml
new file mode 100644
index 0000000..2c1f300
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume.xml
@@ -0,0 +1,44 @@
+<?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.
+-->
+
+<level-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:maxLevel="0" android:drawable="@drawable/ic_ambient_volume_0_0" />
+ <item android:maxLevel="1" android:drawable="@drawable/ic_ambient_volume_0_1" />
+ <item android:maxLevel="2" android:drawable="@drawable/ic_ambient_volume_0_2" />
+ <item android:maxLevel="3" android:drawable="@drawable/ic_ambient_volume_0_3" />
+ <item android:maxLevel="4" android:drawable="@drawable/ic_ambient_volume_0_4" />
+ <item android:maxLevel="5" android:drawable="@drawable/ic_ambient_volume_1_0" />
+ <item android:maxLevel="6" android:drawable="@drawable/ic_ambient_volume_1_1" />
+ <item android:maxLevel="7" android:drawable="@drawable/ic_ambient_volume_1_2" />
+ <item android:maxLevel="8" android:drawable="@drawable/ic_ambient_volume_1_3" />
+ <item android:maxLevel="9" android:drawable="@drawable/ic_ambient_volume_1_4" />
+ <item android:maxLevel="10" android:drawable="@drawable/ic_ambient_volume_2_0" />
+ <item android:maxLevel="11" android:drawable="@drawable/ic_ambient_volume_2_1" />
+ <item android:maxLevel="12" android:drawable="@drawable/ic_ambient_volume_2_2" />
+ <item android:maxLevel="13" android:drawable="@drawable/ic_ambient_volume_2_3" />
+ <item android:maxLevel="14" android:drawable="@drawable/ic_ambient_volume_2_4" />
+ <item android:maxLevel="15" android:drawable="@drawable/ic_ambient_volume_3_0" />
+ <item android:maxLevel="16" android:drawable="@drawable/ic_ambient_volume_3_1" />
+ <item android:maxLevel="17" android:drawable="@drawable/ic_ambient_volume_3_2" />
+ <item android:maxLevel="18" android:drawable="@drawable/ic_ambient_volume_3_3" />
+ <item android:maxLevel="19" android:drawable="@drawable/ic_ambient_volume_3_4" />
+ <item android:maxLevel="20" android:drawable="@drawable/ic_ambient_volume_4_0" />
+ <item android:maxLevel="21" android:drawable="@drawable/ic_ambient_volume_4_1" />
+ <item android:maxLevel="22" android:drawable="@drawable/ic_ambient_volume_4_2" />
+ <item android:maxLevel="23" android:drawable="@drawable/ic_ambient_volume_4_3" />
+ <item android:maxLevel="24" android:drawable="@drawable/ic_ambient_volume_4_4" />
+</level-list>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_0_0.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_0_0.xml
new file mode 100644
index 0000000..738fe89
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_0_0.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M792,904L671,783Q646,799 618,810.5Q590,822 560,829L560,747Q574,742 587.5,737Q601,732 613,725L480,592L480,800L280,600L120,600L120,360L248,360L56,168L112,112L848,848L792,904ZM784,672L726,614Q743,583 751.5,549Q760,515 760,479Q760,385 705,311Q650,237 560,211L560,129Q684,157 762,254.5Q840,352 840,479Q840,532 825.5,581Q811,630 784,672ZM650,538L560,448L560,318Q607,340 633.5,384Q660,428 660,480Q660,495 657.5,509.5Q655,524 650,538ZM480,368L376,264L480,160L480,368ZM400,606L400,512L328,440L328,440L200,440L200,520L314,520L400,606ZM364,476L364,476L364,476L364,476L364,476L364,476L364,476Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_0_1.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_0_1.xml
new file mode 100644
index 0000000..8de24f9
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_0_1.xml
@@ -0,0 +1,27 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_0_2.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_0_2.xml
new file mode 100644
index 0000000..267093e
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_0_2.xml
@@ -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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_0_3.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_0_3.xml
new file mode 100644
index 0000000..fb21254
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_0_3.xml
@@ -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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.558,2.08L11.787,1.606L11.073,4.187L12.844,4.661L13.558,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.038,4.63L15.742,3.334L13.86,5.22L15.157,6.517L17.038,4.63Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.77,9.105L18.515,7.289L15.858,7.622L16.113,9.438L18.77,9.105Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.099,13.419L18.635,11.666L16.103,10.905L15.567,12.658L18.099,13.419Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_0_4.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_0_4.xml
new file mode 100644
index 0000000..4f1e054
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_0_4.xml
@@ -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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.917,0.761L12.146,0.286L11.073,4.192L12.844,4.666L13.917,0.761Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.558,2.08L11.787,1.606L11.073,4.187L12.844,4.661L13.558,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.002,3.661L16.706,2.365L13.86,5.211L15.157,6.507L18.002,3.661Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.038,4.63L15.742,3.334L13.86,5.22L15.157,6.517L17.038,4.63Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M20.123,8.938L19.868,7.123L15.858,7.632L16.113,9.447L20.123,8.938Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.77,9.105L18.515,7.289L15.858,7.622L16.113,9.438L18.77,9.105Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M19.443,13.831L19.979,12.078L16.103,10.898L15.567,12.651L19.443,13.831Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.099,13.419L18.635,11.666L16.103,10.905L15.567,12.658L18.099,13.419Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_1_0.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_1_0.xml
new file mode 100644
index 0000000..d151fee
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_1_0.xml
@@ -0,0 +1,27 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.937L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_1_1.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_1_1.xml
new file mode 100644
index 0000000..3e97a2e
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_1_1.xml
@@ -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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.937L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_1_2.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_1_2.xml
new file mode 100644
index 0000000..7bfd662
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_1_2.xml
@@ -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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.937L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_1_3.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_1_3.xml
new file mode 100644
index 0000000..bba33b3
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_1_3.xml
@@ -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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.558,2.08L11.787,1.606L11.073,4.187L12.844,4.661L13.558,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.038,4.63L15.742,3.334L13.86,5.22L15.157,6.517L17.038,4.63Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.77,9.105L18.515,7.289L15.858,7.622L16.113,9.438L18.77,9.105Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.099,13.419L18.635,11.666L16.103,10.905L15.567,12.658L18.099,13.419Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.937L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_1_4.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_1_4.xml
new file mode 100644
index 0000000..c004316
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_1_4.xml
@@ -0,0 +1,43 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.917,0.761L12.146,0.286L11.073,4.192L12.844,4.666L13.917,0.761Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.558,2.08L11.787,1.606L11.073,4.187L12.844,4.661L13.558,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.002,3.661L16.706,2.365L13.86,5.211L15.157,6.507L18.002,3.661Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.038,4.63L15.742,3.334L13.86,5.22L15.157,6.517L17.038,4.63Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M20.123,8.938L19.868,7.123L15.858,7.632L16.113,9.447L20.123,8.938Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.77,9.105L18.515,7.289L15.858,7.622L16.113,9.438L18.77,9.105Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M19.443,13.831L19.979,12.078L16.103,10.898L15.567,12.651L19.443,13.831Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.099,13.419L18.635,11.666L16.103,10.905L15.567,12.658L18.099,13.419Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.937L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_2_0.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_2_0.xml
new file mode 100644
index 0000000..d89ed87
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_2_0.xml
@@ -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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.765,3.415L9.536,2.94L9.887,4.194L8.116,4.669L7.765,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.941,5.574L6.238,4.278L7.162,5.202L5.866,6.498L4.941,5.574Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.658,9.284L3.913,7.469L5.163,7.625L4.908,9.44L3.658,9.284Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.254,13.058L3.718,11.304L4.923,10.942L5.459,12.695L4.254,13.058Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.937L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_2_1.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_2_1.xml
new file mode 100644
index 0000000..e0a9c41
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_2_1.xml
@@ -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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.765,3.415L9.536,2.94L9.887,4.194L8.116,4.669L7.765,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.941,5.574L6.238,4.278L7.162,5.202L5.866,6.498L4.941,5.574Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.658,9.284L3.913,7.469L5.163,7.625L4.908,9.44L3.658,9.284Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.254,13.058L3.718,11.304L4.923,10.942L5.459,12.695L4.254,13.058Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.937L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_2_2.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_2_2.xml
new file mode 100644
index 0000000..8627af9
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_2_2.xml
@@ -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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.765,3.415L9.536,2.94L9.887,4.194L8.116,4.669L7.765,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.941,5.574L6.238,4.278L7.162,5.202L5.866,6.498L4.941,5.574Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.658,9.284L3.913,7.469L5.163,7.625L4.908,9.44L3.658,9.284Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.254,13.058L3.718,11.304L4.923,10.942L5.459,12.695L4.254,13.058Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.937L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_2_3.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_2_3.xml
new file mode 100644
index 0000000..2c1139a
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_2_3.xml
@@ -0,0 +1,43 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.558,2.08L11.787,1.606L11.073,4.187L12.844,4.661L13.558,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.038,4.63L15.742,3.334L13.86,5.22L15.157,6.517L17.038,4.63Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.77,9.105L18.515,7.289L15.858,7.622L16.113,9.438L18.77,9.105Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.099,13.419L18.635,11.666L16.103,10.905L15.567,12.658L18.099,13.419Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.765,3.415L9.536,2.94L9.887,4.194L8.116,4.669L7.765,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.941,5.574L6.238,4.278L7.162,5.202L5.866,6.498L4.941,5.574Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.658,9.284L3.913,7.469L5.163,7.625L4.908,9.44L3.658,9.284Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.254,13.058L3.718,11.304L4.923,10.942L5.459,12.695L4.254,13.058Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.937L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_2_4.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_2_4.xml
new file mode 100644
index 0000000..8d920b1
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_2_4.xml
@@ -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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.917,0.761L12.146,0.286L11.073,4.192L12.844,4.666L13.917,0.761Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.558,2.08L11.787,1.606L11.073,4.187L12.844,4.661L13.558,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.002,3.661L16.706,2.365L13.86,5.211L15.157,6.507L18.002,3.661Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.038,4.63L15.742,3.334L13.86,5.22L15.157,6.517L17.038,4.63Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M20.123,8.938L19.868,7.123L15.858,7.632L16.113,9.447L20.123,8.938Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.77,9.105L18.515,7.289L15.858,7.622L16.113,9.438L18.77,9.105Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M19.443,13.831L19.979,12.078L16.103,10.898L15.567,12.651L19.443,13.831Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.099,13.419L18.635,11.666L16.103,10.905L15.567,12.658L18.099,13.419Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.765,3.415L9.536,2.94L9.887,4.194L8.116,4.669L7.765,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.941,5.574L6.238,4.278L7.162,5.202L5.866,6.498L4.941,5.574Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.658,9.284L3.913,7.469L5.163,7.625L4.908,9.44L3.658,9.284Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.254,13.058L3.718,11.304L4.923,10.942L5.459,12.695L4.254,13.058Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.937L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_3_0.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_3_0.xml
new file mode 100644
index 0000000..7fd4e17
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_3_0.xml
@@ -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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.043,0.761L8.814,0.286L9.887,4.192L8.116,4.666L7.043,0.761Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.402,2.08L9.173,1.606L9.887,4.187L8.116,4.661L7.402,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.765,3.415L9.536,2.94L9.887,4.194L8.116,4.669L7.765,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.02,3.652L4.317,2.355L7.162,5.201L5.866,6.497L3.02,3.652Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.985,4.621L5.281,3.324L7.163,5.21L5.866,6.507L3.985,4.621Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.941,5.574L6.238,4.278L7.162,5.202L5.866,6.498L4.941,5.574Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M0.898,8.942L1.153,7.127L5.163,7.636L4.908,9.451L0.898,8.942Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.251,9.109L2.506,7.293L5.163,7.626L4.908,9.442L2.251,9.109Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.658,9.284L3.913,7.469L5.163,7.625L4.908,9.44L3.658,9.284Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.382L4.666,7.566L5.162,7.627L4.907,9.46L4.411,9.382Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M1.589,13.874L1.053,12.121L4.93,10.941L5.466,12.694L1.589,13.874Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.933,13.463L2.397,11.71L4.93,10.948L5.466,12.702L2.933,13.463Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.254,13.058L3.718,11.304L4.923,10.942L5.459,12.695L4.254,13.058Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.936L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_3_1.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_3_1.xml
new file mode 100644
index 0000000..f0f8150
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_3_1.xml
@@ -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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.402,2.08L9.173,1.606L9.887,4.187L8.116,4.661L7.402,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.765,3.415L9.536,2.94L9.887,4.194L8.116,4.669L7.765,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.985,4.621L5.281,3.324L7.163,5.21L5.866,6.507L3.985,4.621Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.941,5.574L6.238,4.278L7.162,5.202L5.866,6.498L4.941,5.574Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.251,9.109L2.506,7.293L5.163,7.626L4.908,9.442L2.251,9.109Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.658,9.284L3.913,7.469L5.163,7.625L4.908,9.44L3.658,9.284Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.381L4.666,7.566L5.162,7.627L4.907,9.459L4.411,9.381Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.933,13.463L2.397,11.71L4.93,10.948L5.466,12.702L2.933,13.463Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.254,13.058L3.718,11.304L4.923,10.942L5.459,12.695L4.254,13.058Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.936L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_3_2.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_3_2.xml
new file mode 100644
index 0000000..1bb2017
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_3_2.xml
@@ -0,0 +1,43 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.652L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.402,2.08L9.173,1.606L9.887,4.187L8.116,4.661L7.402,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.765,3.415L9.536,2.94L9.887,4.194L8.116,4.669L7.765,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.985,4.621L5.281,3.324L7.163,5.21L5.866,6.507L3.985,4.621Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.941,5.574L6.238,4.278L7.162,5.202L5.866,6.498L4.941,5.574Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.251,9.109L2.506,7.293L5.163,7.626L4.908,9.442L2.251,9.109Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.658,9.284L3.913,7.469L5.163,7.625L4.908,9.44L3.658,9.284Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.381L4.666,7.566L5.162,7.627L4.907,9.459L4.411,9.381Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.933,13.463L2.397,11.71L4.93,10.948L5.466,12.702L2.933,13.463Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.254,13.058L3.718,11.304L4.923,10.942L5.459,12.695L4.254,13.058Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.936L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_3_3.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_3_3.xml
new file mode 100644
index 0000000..a8bc0af
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_3_3.xml
@@ -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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.558,2.08L11.787,1.606L11.073,4.187L12.844,4.661L13.558,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.038,4.63L15.742,3.334L13.86,5.22L15.157,6.517L17.038,4.63Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.77,9.105L18.515,7.289L15.858,7.622L16.113,9.438L18.77,9.105Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.099,13.419L18.635,11.666L16.103,10.905L15.567,12.658L18.099,13.419Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.652L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.402,2.08L9.173,1.606L9.887,4.187L8.116,4.661L7.402,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.765,3.415L9.536,2.94L9.887,4.194L8.116,4.669L7.765,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.985,4.621L5.281,3.324L7.163,5.21L5.866,6.507L3.985,4.621Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.941,5.574L6.238,4.278L7.162,5.202L5.866,6.498L4.941,5.574Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.251,9.109L2.506,7.293L5.163,7.626L4.908,9.442L2.251,9.109Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.658,9.284L3.913,7.469L5.163,7.625L4.908,9.44L3.658,9.284Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.381L4.666,7.566L5.162,7.627L4.907,9.459L4.411,9.381Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.933,13.463L2.397,11.71L4.93,10.948L5.466,12.702L2.933,13.463Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.254,13.058L3.718,11.304L4.923,10.942L5.459,12.695L4.254,13.058Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.936L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_3_4.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_3_4.xml
new file mode 100644
index 0000000..f8a5832
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_3_4.xml
@@ -0,0 +1,51 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.917,0.761L12.146,0.286L11.073,4.192L12.844,4.666L13.917,0.761Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.558,2.08L11.787,1.606L11.073,4.187L12.844,4.661L13.558,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.002,3.661L16.706,2.365L13.86,5.211L15.157,6.507L18.002,3.661Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.038,4.63L15.742,3.334L13.86,5.22L15.157,6.517L17.038,4.63Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M20.123,8.938L19.868,7.123L15.858,7.632L16.113,9.447L20.123,8.938Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.77,9.105L18.515,7.289L15.858,7.622L16.113,9.438L18.77,9.105Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M19.443,13.831L19.979,12.078L16.103,10.898L15.567,12.651L19.443,13.831Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.099,13.419L18.635,11.666L16.103,10.905L15.567,12.658L18.099,13.419Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.652L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.402,2.08L9.173,1.606L9.887,4.187L8.116,4.661L7.402,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.765,3.415L9.536,2.94L9.887,4.194L8.116,4.669L7.765,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M7.98,4.19L9.751,3.716L9.887,4.19L8.116,4.665L7.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.985,4.621L5.281,3.324L7.162,5.21L5.866,6.507L3.985,4.621Z"/>
+ <path android:fillAlpha="0.4" android:fillColor="#D9D9D9" android:pathData="M4.941,5.574L6.238,4.278L7.162,5.202L5.866,6.498L4.941,5.574Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M5.523,6.152L6.82,4.856L7.165,5.198L5.869,6.494L5.523,6.152Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.251,9.109L2.506,7.293L5.163,7.626L4.908,9.442L2.251,9.109Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M3.658,9.284L3.913,7.469L5.163,7.625L4.908,9.44L3.658,9.284Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.411,9.381L4.666,7.566L5.162,7.627L4.907,9.459L4.411,9.381Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.933,13.463L2.397,11.71L4.93,10.948L5.466,12.702L2.933,13.463Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.254,13.058L3.718,11.304L4.923,10.942L5.459,12.695L4.254,13.058Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M4.919,12.853L4.383,11.1L4.919,10.936L5.455,12.69L4.919,12.853Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_4_0.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_4_0.xml
new file mode 100644
index 0000000..e5d7ad4
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_4_0.xml
@@ -0,0 +1,27 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M6.983,0.765L8.754,0.29L9.947,4.192L8.177,4.666L6.983,0.765Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.974,3.583L4.271,2.287L7.16,5.211L5.864,6.507L2.974,3.583Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M0.879,8.868L1.134,7.052L5.163,7.632L4.908,9.447L0.879,8.868Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M1.601,14.021L1.065,12.268L4.918,10.898L5.454,12.651L1.601,14.021Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_4_1.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_4_1.xml
new file mode 100644
index 0000000..f5cdf5d
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_4_1.xml
@@ -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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M6.983,0.765L8.754,0.29L9.947,4.192L8.177,4.666L6.983,0.765Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.974,3.583L4.271,2.287L7.16,5.211L5.864,6.507L2.974,3.583Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M0.879,8.868L1.134,7.052L5.163,7.632L4.908,9.447L0.879,8.868Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M1.601,14.021L1.065,12.268L4.918,10.898L5.454,12.651L1.601,14.021Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_4_2.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_4_2.xml
new file mode 100644
index 0000000..cbed634
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_4_2.xml
@@ -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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M6.983,0.765L8.754,0.29L9.947,4.192L8.177,4.666L6.983,0.765Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.974,3.583L4.271,2.287L7.16,5.211L5.864,6.507L2.974,3.583Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M0.879,8.868L1.134,7.052L5.163,7.632L4.908,9.447L0.879,8.868Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M1.601,14.021L1.065,12.268L4.918,10.898L5.454,12.651L1.601,14.021Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_4_3.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_4_3.xml
new file mode 100644
index 0000000..90d81d8
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_4_3.xml
@@ -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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M6.983,0.765L8.754,0.29L9.947,4.192L8.177,4.666L6.983,0.765Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.974,3.583L4.271,2.287L7.16,5.211L5.864,6.507L2.974,3.583Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M0.879,8.868L1.134,7.052L5.163,7.632L4.908,9.447L0.879,8.868Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M1.601,14.021L1.065,12.268L4.918,10.898L5.454,12.651L1.601,14.021Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.558,2.08L11.787,1.606L11.073,4.187L12.844,4.661L13.558,2.08Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.038,4.63L15.742,3.334L13.86,5.22L15.157,6.517L17.038,4.63Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.77,9.105L18.515,7.289L15.858,7.622L16.113,9.438L18.77,9.105Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.099,13.419L18.635,11.666L16.103,10.905L15.566,12.658L18.099,13.419Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_ambient_volume_4_4.xml b/packages/SettingsLib/res/drawable/ic_ambient_volume_4_4.xml
new file mode 100644
index 0000000..f1a9a8a
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_ambient_volume_4_4.xml
@@ -0,0 +1,43 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="22"
+ android:viewportHeight="22">
+ <path android:fillColor="#ffffff" android:pathData="M17.987,18.375V20.987H3.013V18.375C3.013,17.862 3.145,17.379 3.409,16.928C3.674,16.477 4.039,16.135 4.506,15.902L4.506,15.902C5.302,15.496 6.199,15.153 7.199,14.872C8.197,14.591 9.298,14.45 10.5,14.45C11.702,14.45 12.802,14.591 13.801,14.872C14.8,15.153 15.698,15.496 16.494,15.902L16.494,15.902C16.961,16.135 17.326,16.477 17.59,16.928C17.855,17.379 17.987,17.862 17.987,18.375ZM10.5,13.487C9.472,13.487 8.593,13.121 7.861,12.389C7.129,11.657 6.763,10.778 6.763,9.75C6.763,8.722 7.129,7.843 7.861,7.111C8.593,6.379 9.472,6.013 10.5,6.013C11.528,6.013 12.407,6.379 13.139,7.111C13.871,7.843 14.237,8.722 14.237,9.75C14.237,10.778 13.871,11.657 13.139,12.389C12.407,13.121 11.528,13.487 10.5,13.487Z" android:strokeColor="#ffffff" android:strokeWidth="0.0255682"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M6.983,0.765L8.754,0.29L9.947,4.192L8.177,4.666L6.983,0.765Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M2.975,3.583L4.271,2.287L7.16,5.211L5.864,6.507L2.975,3.583Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M0.878,8.868L1.134,7.052L5.163,7.632L4.907,9.447L0.878,8.868Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M1.601,14.021L1.065,12.268L4.918,10.898L5.454,12.651L1.601,14.021Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.917,0.761L12.146,0.286L11.073,4.192L12.844,4.666L13.917,0.761Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M18.002,3.661L16.706,2.365L13.86,5.211L15.157,6.507L18.002,3.661Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M20.123,8.938L19.868,7.123L15.858,7.632L16.113,9.447L20.123,8.938Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M19.443,13.831L19.979,12.078L16.103,10.898L15.567,12.651L19.443,13.831Z"/>
+ <path android:fillAlpha="0.4" android:fillColor="#D9D9D9" android:pathData="M13.558,2.08L11.787,1.606L11.073,4.187L12.844,4.661L13.558,2.08Z" android:strokeAlpha="0.4"/>
+ <path android:fillAlpha="0.4" android:fillColor="#D9D9D9" android:pathData="M17.038,4.63L15.742,3.334L13.86,5.22L15.157,6.517L17.038,4.63Z" android:strokeAlpha="0.4"/>
+ <path android:fillAlpha="0.4" android:fillColor="#D9D9D9" android:pathData="M18.77,9.105L18.515,7.289L15.858,7.622L16.113,9.438L18.77,9.105Z" android:strokeAlpha="0.4"/>
+ <path android:fillAlpha="0.4" android:fillColor="#D9D9D9" android:pathData="M18.099,13.419L18.635,11.666L16.103,10.905L15.567,12.658L18.099,13.419Z" android:strokeAlpha="0.4"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M13.195,3.415L11.424,2.94L11.073,4.194L12.844,4.669L13.195,3.415Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.081,5.584L14.785,4.288L13.86,5.212L15.157,6.508L16.081,5.584Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M17.363,9.28L17.108,7.465L15.858,7.621L16.113,9.436L17.363,9.28Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.778,13.014L17.314,11.261L16.109,10.898L15.573,12.651L16.778,13.014Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M12.98,4.19L11.209,3.716L11.073,4.19L12.844,4.665L12.98,4.19Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M15.5,6.162L14.203,4.866L13.858,5.208L15.154,6.504L15.5,6.162Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.61,9.378L16.355,7.562L15.859,7.623L16.114,9.456L16.61,9.378Z"/>
+ <path android:fillColor="#D9D9D9" android:pathData="M16.113,12.81L16.649,11.057L16.113,10.893L15.577,12.646L16.113,12.81Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index ae04ca1..ac436ad 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -737,7 +737,7 @@
<string name="bt_le_audio_broadcast_dialog_different_output" msgid="2638402023060391333">"Verander uitvoer"</string>
<string name="back_navigation_animation" msgid="8105467568421689484">"Voorspellingteruggebaaranimasies"</string>
<string name="back_navigation_animation_summary" msgid="741292224121599456">"Aktiveer stelselanimasies vir voorspellingteruggebaar."</string>
- <string name="back_navigation_animation_dialog" msgid="8696966520944625596">"Hierdie instelling aktiveer stelselanimasies vir voorspellinggebaaranimasie. Dit vereis dat enableOnBackInvokedCallback per program op waar gestel word in die manifeslêer."</string>
+ <string name="back_navigation_animation_dialog" msgid="8696966520944625596">"Hierdie instelling aktiveer stelselanimasies vir voorspellinggebaaranimasie. Dit vereis dat enableOnBackInvokedCallback per app op waar gestel word in die manifeslêer."</string>
<string name="font_scale_percentage" msgid="2624057443622817886">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="not_specified" msgid="5423502443185110328">"Nie gespesifiseer nie"</string>
<string name="neuter" msgid="2075249330106127310">"Neutrum"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 690add8..a85d0cc 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -638,7 +638,7 @@
<string name="user_setup_dialog_title" msgid="8037342066381939995">"ಈಗ ಬಳಕೆದಾರರನ್ನು ಸೆಟ್ ಮಾಡಬೇಕೆ?"</string>
<string name="user_setup_dialog_message" msgid="269931619868102841">"ಸಾಧನವನ್ನು ತೆಗೆದುಕೊಳ್ಳಲು ಮತ್ತು ಅದರ ಸ್ಥಳವನ್ನು ಹೊಂದಿಸಲು ವ್ಯಕ್ತಿಯು ಲಭ್ಯವಿದ್ದಾರೆಯೇ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ"</string>
<string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"ಇದೀಗ ಪ್ರೊಫೈಲ್ ಅನ್ನು ಹೊಂದಿಸುವುದೇ?"</string>
- <string name="user_setup_button_setup_now" msgid="1708269547187760639">"ಇದೀಗ ಹೊಂದಿಸಿ"</string>
+ <string name="user_setup_button_setup_now" msgid="1708269547187760639">"ಇದೀಗ ಸೆಟ್ ಮಾಡಿ"</string>
<string name="user_setup_button_setup_later" msgid="8712980133555493516">"ಈಗಲೇ ಬೇಡ"</string>
<string name="user_add_user_type_title" msgid="551279664052914497">"ಸೇರಿಸಿ"</string>
<string name="user_new_user_name" msgid="60979820612818840">"ಹೊಸ ಬಳಕೆದಾರರು"</string>
@@ -646,7 +646,7 @@
<string name="user_info_settings_title" msgid="6351390762733279907">"ಬಳಕೆದಾರರ ಮಾಹಿತಿ"</string>
<string name="profile_info_settings_title" msgid="105699672534365099">"ಪ್ರೊಫೈಲ್ ಮಾಹಿತಿ"</string>
<string name="user_need_lock_message" msgid="4311424336209509301">"ನೀವು ನಿರ್ಬಂಧಿತ ಪ್ರೊಫೈಲ್ ಅನ್ನು ರಚಿಸಬಹುದಾದರ ಮೊದಲು, ನಿಮ್ಮ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ರಕ್ಷಿಸಲು ನೀವು ಪರದೆಯ ಲಾಕ್ ಹೊಂದಿಸುವ ಅಗತ್ಯವಿದೆ."</string>
- <string name="user_set_lock_button" msgid="1427128184982594856">"ಲಾಕ್ ಹೊಂದಿಸಿ"</string>
+ <string name="user_set_lock_button" msgid="1427128184982594856">"ಲಾಕ್ ಸೆಟ್ ಮಾಡಿ"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>ಗೆ ಬದಲಿಸಿ"</string>
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ…"</string>
<string name="creating_new_guest_dialog_message" msgid="1114905602181350690">"ಹೊಸ ಅತಿಥಿಯನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ…"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 0d27f00..8942737 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -601,7 +601,7 @@
<string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"ଏଠାରେ ପ୍ଲେ କରିବା ପାଇଁ ଡିଭାଇସକୁ ସକ୍ରିୟ କରନ୍ତୁ"</string>
<string name="media_output_status_unauthorized" msgid="5880222828273853838">"ପ୍ଲେ କରିବା ପାଇଁ ଡିଭାଇସକୁ ଅନୁମୋଦନ କରାଯାଇନାହିଁ"</string>
<string name="media_output_status_track_unsupported" msgid="5576313219317709664">"ଏଠାରେ ମିଡିଆ ପ୍ଲେ କରାଯାଇପାରିବ ନାହିଁ"</string>
- <string name="tv_media_transfer_connected" msgid="5145011475885290725">"କନେକ୍ଟ କରାଯାଇଛି"</string>
+ <string name="tv_media_transfer_connected" msgid="5145011475885290725">"କନେକ୍ଟ ହୋଇଛି"</string>
<string name="tv_media_transfer_arc_fallback_title" msgid="3674360098755328601">"HDMI ARC"</string>
<string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
<string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC ମାଧ୍ୟମରେ କନେକ୍ଟ କରାଯାଇଛି"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 504d821..2676d0e 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -313,7 +313,7 @@
<string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4898693684282596143">"Aktivizo kodekun e audios me Bluetooth\nZgjedhja: Bite për shembull"</string>
<string name="bluetooth_select_a2dp_codec_channel_mode" msgid="364277285688014427">"Modaliteti i kanalit të audios me Bluetooth"</string>
<string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="2076949781460359589">"Aktivizo kodekun e audios me Bluetooth\nZgjedhja: Modaliteti i kanalit"</string>
- <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3233402355917446304">"Kodeku LDAC i audios së Bluetooth-it: Cilësia e luajtjes"</string>
+ <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3233402355917446304">"Kodeku LDAC i audios me Bluetooth: Cilësia e luajtjes"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="7274396574659784285">"Aktivizo LDAC të audios me Bluetooth\nZgjedhja e kodekut: Cilësia e luajtjes"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="2040810756832027227">"Transmetimi: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
<string name="select_private_dns_configuration_title" msgid="7887550926056143018">"DNS-ja private"</string>
@@ -586,8 +586,7 @@
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ky telefon"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ky tablet"</string>
<string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Ky kompjuter (i brendshëm)"</string>
- <!-- no translation found for media_transfer_this_device_name_tv (8508713779441163887) -->
- <skip />
+ <string name="media_transfer_this_device_name_tv" msgid="8508713779441163887">"Ky televizor"</string>
<string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Altoparlanti i stacionit"</string>
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Pajisja e jashtme"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Pajisja e lidhur"</string>
@@ -607,10 +606,8 @@
<string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
<string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Lidhur përmes ARC-së"</string>
<string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Lidhur përmes eARC-së"</string>
- <!-- no translation found for tv_media_transfer_internal_speakers (4662765121700213785) -->
- <skip />
- <!-- no translation found for tv_media_transfer_hdmi_title (6715658310934507444) -->
- <skip />
+ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Altoparlanti i integruar"</string>
+ <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audioja e televizorit"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem me lidhjen. Fike dhe ndize përsëri pajisjen"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Pajisja audio me tel"</string>
<string name="help_label" msgid="3528360748637781274">"Ndihma dhe komentet"</string>
diff --git a/packages/SettingsLib/res/values-uk/arrays.xml b/packages/SettingsLib/res/values-uk/arrays.xml
index 6032efb..234af36 100644
--- a/packages/SettingsLib/res/values-uk/arrays.xml
+++ b/packages/SettingsLib/res/values-uk/arrays.xml
@@ -257,12 +257,12 @@
<item msgid="1212561935004167943">"Тест. команди малювання зеленим"</item>
</string-array>
<string-array name="track_frame_time_entries">
- <item msgid="634406443901014984">"Вимк."</item>
+ <item msgid="634406443901014984">"Вимкнено"</item>
<item msgid="1288760936356000927">"На екрані у вигляді смужок"</item>
<item msgid="5023908510820531131">"У команді \"<xliff:g id="AS_TYPED_COMMAND">adb shell dumpsys gfxinfo</xliff:g>\""</item>
</string-array>
<string-array name="debug_hw_overdraw_entries">
- <item msgid="1968128556747588800">"Вимк."</item>
+ <item msgid="1968128556747588800">"Вимкнено"</item>
<item msgid="3033215374382962216">"Показувати області накладання"</item>
<item msgid="3474333938380896988">"Показувати області дейтераномалії"</item>
</string-array>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 216574a..429e4c9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -631,15 +631,15 @@
assistantProfile.getAllConnectedDevices().stream()
.map(deviceManager::findDevice)
.filter(Objects::nonNull)
- .map(CachedBluetoothDevice::getGroupId)
+ .map(BluetoothUtils::getGroupId)
.collect(Collectors.toSet());
Set<Integer> activeGroupIds =
leAudioProfile.getActiveDevices().stream()
.map(deviceManager::findDevice)
.filter(Objects::nonNull)
- .map(CachedBluetoothDevice::getGroupId)
+ .map(BluetoothUtils::getGroupId)
.collect(Collectors.toSet());
- int groupId = cachedDevice.getGroupId();
+ int groupId = getGroupId(cachedDevice);
return activeGroupIds.size() == 1
&& !activeGroupIds.contains(groupId)
&& connectedGroupIds.size() == 2
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 4eb0567..b58983f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -17,6 +17,7 @@
package com.android.settingslib.bluetooth;
import static com.android.settingslib.flags.Flags.enableSetPreferredTransportForLeAudioDevice;
+import static com.android.settingslib.flags.Flags.ignoreA2dpDisconnectionForAndroidAuto;
import android.annotation.CallbackExecutor;
import android.annotation.StringRes;
@@ -82,6 +83,8 @@
*/
public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
private static final String TAG = "CachedBluetoothDevice";
+ private static final ParcelUuid ANDROID_AUTO_UUID =
+ ParcelUuid.fromString("4de17a00-52cb-11e6-bdf4-0800200c9a66");
// See mConnectAttempted
private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
@@ -260,18 +263,26 @@
if (mHandler.hasMessages(profile.getProfileId())) {
mHandler.removeMessages(profile.getProfileId());
if (profile.getConnectionPolicy(mDevice) >
- BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
- /*
- * If we received state DISCONNECTED and previous state was
- * CONNECTING and connection policy is FORBIDDEN or UNKNOWN
- * then it's not really a failure to connect.
- *
- * Connection profile is considered as failed when connection
- * policy indicates that profile should be connected
- * but it got disconnected.
- */
- Log.w(TAG, "onProfileStateChanged(): Failed to connect profile");
- setProfileConnectedStatus(profile.getProfileId(), true);
+ BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
+ if (ignoreA2dpDisconnectionForAndroidAuto()
+ && profile instanceof A2dpProfile && isAndroidAuto()) {
+ Log.w(TAG,
+ "onProfileStateChanged(): Skip setting A2DP "
+ + "connection fail for Android Auto");
+ } else {
+ /*
+ * If we received state DISCONNECTED and previous state was
+ * CONNECTING and connection policy is FORBIDDEN or UNKNOWN
+ * then it's not really a failure to connect.
+ *
+ * Connection profile is considered as failed when connection
+ * policy indicates that profile should be connected
+ * but it got disconnected.
+ */
+ Log.w(TAG,
+ "onProfileStateChanged(): Failed to connect profile");
+ setProfileConnectedStatus(profile.getProfileId(), true);
+ }
}
}
break;
@@ -2031,4 +2042,16 @@
void setLocalBluetoothManager(LocalBluetoothManager bluetoothManager) {
mBluetoothManager = bluetoothManager;
}
+
+ private boolean isAndroidAuto() {
+ try {
+ ParcelUuid[] uuids = mDevice.getUuids();
+ if (ArrayUtils.contains(uuids, ANDROID_AUTO_UUID)) {
+ return true;
+ }
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Fail to check isAndroidAuto for " + this);
+ }
+ return false;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
index 76aa5bf..478a5d1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
@@ -80,8 +80,14 @@
// behavior.
@AudioDeviceType int deviceTypeToActivate = mSelectedInputDeviceType;
for (AudioDeviceInfo info : addedDevices) {
- if (InputMediaDevice.isSupportedInputDevice(info.getType())) {
- deviceTypeToActivate = info.getType();
+ @AudioDeviceType int type = info.getType();
+ // Since onAudioDevicesAdded is called not only when new device is hot
+ // plugged, but also when the switcher dialog is opened, make sure to check
+ // against existing device list and only activate if the device does not
+ // exist previously.
+ if (InputMediaDevice.isSupportedInputDevice(type)
+ && findDeviceByType(type) == null) {
+ deviceTypeToActivate = type;
}
}
@@ -140,16 +146,22 @@
}
// TODO(b/355684672): handle edge case where there are two devices with the same type. Only
- // using a single mSelectedInputDeviceType might not be enough to recognize the correct device.
- public @Nullable MediaDevice getSelectedInputDevice() {
+ // using a single type might not be enough to recognize the correct device.
+ @Nullable
+ private MediaDevice findDeviceByType(@AudioDeviceType int type) {
for (MediaDevice device : mInputMediaDevices) {
- if (((InputMediaDevice) device).getAudioDeviceInfoType() == mSelectedInputDeviceType) {
+ if (((InputMediaDevice) device).getAudioDeviceInfoType() == type) {
return device;
}
}
return null;
}
+ @Nullable
+ public MediaDevice getSelectedInputDevice() {
+ return findDeviceByType(mSelectedInputDeviceType);
+ }
+
private void applyDefaultSelectedTypeToAllPresets() {
mSelectedInputDeviceType = retrieveDefaultSelectedDeviceType();
AudioDeviceAttributes deviceAttributes =
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
index 35e3dd3..e1be1d2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
@@ -231,7 +231,7 @@
public SignalStrength signalStrength;
public TelephonyDisplayInfo telephonyDisplayInfo =
new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false);
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false, false, false);
/**
* Empty constructor
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
index d808a25..9c34946 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
@@ -361,6 +361,26 @@
}
@Test
+ public void onAudioDevicesAdded_doNotActivatePreexistingDevice() {
+ final AudioManager audioManager = mock(AudioManager.class);
+ InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager);
+
+ final AudioDeviceInfo info = mockWiredHeadsetInfo();
+ InputMediaDevice device = createInputMediaDeviceFromDeviceInfo(info);
+ inputRouteManager.mInputMediaDevices.add(device);
+
+ // Trigger onAudioDevicesAdded with a device that already exists in the device list.
+ AudioDeviceInfo[] devices = {info};
+ inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices);
+
+ // The device should not be activated.
+ for (@MediaRecorder.Source int preset : PRESETS) {
+ verify(audioManager, never())
+ .setPreferredDeviceForCapturePreset(preset, getWiredHeadsetDeviceAttributes());
+ }
+ }
+
+ @Test
public void onAudioDevicesRemoved_shouldApplyDefaultSelectedDeviceToAllPresets() {
final AudioManager audioManager = mock(AudioManager.class);
InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager);
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 1f291cd..731cb72 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -99,6 +99,7 @@
Settings.Secure.RTT_CALLING_MODE,
Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
Settings.Secure.MINIMAL_POST_PROCESSING_ALLOWED,
+ Settings.Secure.MIRROR_BUILT_IN_DISPLAY,
Settings.Secure.MATCH_CONTENT_FRAME_RATE,
Settings.Secure.NIGHT_DISPLAY_CUSTOM_START_TIME,
Settings.Secure.NIGHT_DISPLAY_CUSTOM_END_TIME,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index abd5b9a..039832c 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -150,6 +150,7 @@
Secure.INCALL_POWER_BUTTON_BEHAVIOR,
new DiscreteValueValidator(new String[] {"1", "2"}));
VALIDATORS.put(Secure.MINIMAL_POST_PROCESSING_ALLOWED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.MIRROR_BUILT_IN_DISPLAY, BOOLEAN_VALIDATOR);
VALIDATORS.put(
Secure.MATCH_CONTENT_FRAME_RATE,
new DiscreteValueValidator(new String[] {"0", "1", "2"}));
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/EventLogTags.logtags b/packages/SettingsProvider/src/com/android/providers/settings/EventLogTags.logtags
index 7eff16b..0367fe0 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/EventLogTags.logtags
+++ b/packages/SettingsProvider/src/com/android/providers/settings/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package com.android.providers.settings;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 9ab853f..326bff4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -22,6 +22,7 @@
import android.app.backup.BackupAgentHelper;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupRestoreEventLogger;
import android.app.backup.FullBackupDataOutput;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -82,6 +83,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
+import java.util.HashMap;
import java.util.zip.CRC32;
/**
@@ -194,6 +196,22 @@
private static final String KEY_LOCK_SETTINGS_PIN_ENHANCED_PRIVACY =
"pin_enhanced_privacy";
+ // Error messages for logging metrics.
+ private static final String ERROR_COULD_NOT_READ_FROM_CURSOR =
+ "could_not_read_from_cursor";
+ private static final String ERROR_FAILED_TO_WRITE_ENTITY =
+ "failed_to_write_entity";
+ private static final String ERROR_COULD_NOT_READ_ENTITY =
+ "could_not_read_entity";
+ private static final String ERROR_SKIPPED_BY_SYSTEM = "skipped_by_system";
+ private static final String ERROR_SKIPPED_BY_BLOCKLIST =
+ "skipped_by_dynamic_blocklist";
+ private static final String ERROR_SKIPPED_PRESERVED = "skipped_preserved";
+ private static final String ERROR_SKIPPED_DUE_TO_LARGE_SCREEN =
+ "skipped_due_to_large_screen";
+ private static final String ERROR_DID_NOT_PASS_VALIDATION = "did_not_pass_validation";
+
+
// Name of the temporary file we use during full backup/restore. This is
// stored in the full-backup tarfile as well, so should not be changed.
private static final String STAGE_FILE = "flattened-data";
@@ -224,6 +242,10 @@
// The font_scale default value for this device.
private float mDefaultFontScale;
+ @Nullable private BackupRestoreEventLogger mBackupRestoreEventLogger;
+ @VisibleForTesting boolean areAgentMetricsEnabled = false;
+ @VisibleForTesting protected Map<String, Integer> numberOfSettingsPerKey;
+
@Override
public void onCreate() {
if (DEBUG_BACKUP) Log.d(TAG, "onCreate() invoked");
@@ -232,6 +254,11 @@
.getStringArray(R.array.entryvalues_font_size);
mSettingsHelper = new SettingsHelper(this);
mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+ if (com.android.server.backup.Flags.enableMetricsSettingsBackupAgents()) {
+ mBackupRestoreEventLogger = this.getBackupRestoreEventLogger();
+ numberOfSettingsPerKey = new HashMap<>();
+ areAgentMetricsEnabled = true;
+ }
super.onCreate();
}
@@ -356,7 +383,7 @@
restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal,
movedToSecure, /* movedToSystem= */ null,
R.array.restore_blocked_system_settings, dynamicBlockList,
- preservedSystemSettings);
+ preservedSystemSettings, KEY_SYSTEM);
mSettingsHelper.applyAudioSettings();
break;
@@ -364,13 +391,13 @@
restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal,
/* movedToSecure= */ null, movedToSystem,
R.array.restore_blocked_secure_settings, dynamicBlockList,
- preservedSecureSettings);
+ preservedSecureSettings, KEY_SECURE);
break;
case KEY_GLOBAL :
restoreSettings(data, Settings.Global.CONTENT_URI, /* movedToGlobal= */ null,
movedToSecure, movedToSystem, R.array.restore_blocked_global_settings,
- dynamicBlockList, preservedGlobalSettings);
+ dynamicBlockList, preservedGlobalSettings, KEY_GLOBAL);
break;
case KEY_WIFI_SUPPLICANT :
@@ -489,7 +516,7 @@
restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI, movedToGlobal,
movedToSecure, /* movedToSystem= */ null,
R.array.restore_blocked_system_settings, Collections.emptySet(),
- Collections.emptySet());
+ Collections.emptySet(), KEY_SYSTEM);
// secure settings
nBytes = in.readInt();
@@ -499,7 +526,7 @@
restoreSettings(buffer, nBytes, Settings.Secure.CONTENT_URI, movedToGlobal,
/* movedToSecure= */ null, movedToSystem,
R.array.restore_blocked_secure_settings, Collections.emptySet(),
- Collections.emptySet());
+ Collections.emptySet(), KEY_SECURE);
// Global only if sufficiently new
if (version >= FULL_BACKUP_ADDED_GLOBAL) {
@@ -510,7 +537,7 @@
restoreSettings(buffer, nBytes, Settings.Global.CONTENT_URI,
/* movedToGlobal= */ null, movedToSecure, movedToSystem,
R.array.restore_blocked_global_settings, Collections.emptySet(),
- Collections.emptySet());
+ Collections.emptySet(), KEY_GLOBAL);
}
// locale
@@ -654,23 +681,41 @@
if (oldChecksum == newChecksum) {
return oldChecksum;
}
+ writeDataForKey(key, data, output);
+ return newChecksum;
+ }
+
+ @VisibleForTesting
+ void writeDataForKey(String key, byte[] data, BackupDataOutput output) {
+ boolean shouldLogMetrics =
+ areAgentMetricsEnabled && numberOfSettingsPerKey.containsKey(key);
try {
if (DEBUG_BACKUP) {
Log.v(TAG, "Writing entity " + key + " of size " + data.length);
}
output.writeEntityHeader(key, data.length);
output.writeEntityData(data, data.length);
+ if (shouldLogMetrics) {
+ mBackupRestoreEventLogger
+ .logItemsBackedUp(key, numberOfSettingsPerKey.get(key));
+ }
} catch (IOException ioe) {
// Bail
+ if (shouldLogMetrics) {
+ mBackupRestoreEventLogger
+ .logItemsBackupFailed(
+ key,
+ numberOfSettingsPerKey.get(key),
+ ERROR_FAILED_TO_WRITE_ENTITY);
+ }
}
- return newChecksum;
}
private byte[] getSystemSettings() {
Cursor cursor = getContentResolver().query(Settings.System.CONTENT_URI, PROJECTION, null,
null, null);
try {
- return extractRelevantValues(cursor, SystemSettings.SETTINGS_TO_BACKUP);
+ return extractRelevantValues(cursor, SystemSettings.SETTINGS_TO_BACKUP, KEY_SYSTEM);
} finally {
cursor.close();
}
@@ -680,7 +725,7 @@
Cursor cursor = getContentResolver().query(Settings.Secure.CONTENT_URI, PROJECTION, null,
null, null);
try {
- return extractRelevantValues(cursor, SecureSettings.SETTINGS_TO_BACKUP);
+ return extractRelevantValues(cursor, SecureSettings.SETTINGS_TO_BACKUP, KEY_SECURE);
} finally {
cursor.close();
}
@@ -690,7 +735,7 @@
Cursor cursor = getContentResolver().query(Settings.Global.CONTENT_URI, PROJECTION, null,
null, null);
try {
- return extractRelevantValues(cursor, getGlobalSettingsToBackup());
+ return extractRelevantValues(cursor, getGlobalSettingsToBackup(), KEY_GLOBAL);
} finally {
cursor.close();
}
@@ -773,7 +818,8 @@
return baos.toByteArray();
}
- private void restoreSettings(
+ @VisibleForTesting
+ void restoreSettings(
BackupDataInput data,
Uri contentUri,
Set<String> movedToGlobal,
@@ -781,12 +827,17 @@
Set<String> movedToSystem,
int blockedSettingsArrayId,
Set<String> dynamicBlockList,
- Set<String> settingsToPreserve) {
+ Set<String> settingsToPreserve,
+ String settingsKey) {
byte[] settings = new byte[data.getDataSize()];
try {
data.readEntityData(settings, 0, settings.length);
} catch (IOException ioe) {
Log.e(TAG, "Couldn't read entity data");
+ if (areAgentMetricsEnabled) {
+ mBackupRestoreEventLogger.logItemsRestoreFailed(
+ settingsKey, /* count= */ 1, ERROR_COULD_NOT_READ_ENTITY);
+ }
return;
}
restoreSettings(
@@ -798,7 +849,8 @@
movedToSystem,
blockedSettingsArrayId,
dynamicBlockList,
- settingsToPreserve);
+ settingsToPreserve,
+ settingsKey);
}
private void restoreSettings(
@@ -810,7 +862,8 @@
Set<String> movedToSystem,
int blockedSettingsArrayId,
Set<String> dynamicBlockList,
- Set<String> settingsToPreserve) {
+ Set<String> settingsToPreserve,
+ String settingsKey) {
restoreSettings(
settings,
0,
@@ -821,7 +874,8 @@
movedToSystem,
blockedSettingsArrayId,
dynamicBlockList,
- settingsToPreserve);
+ settingsToPreserve,
+ settingsKey);
}
@VisibleForTesting
@@ -835,12 +889,13 @@
Set<String> movedToSystem,
int blockedSettingsArrayId,
Set<String> dynamicBlockList,
- Set<String> settingsToPreserve) {
+ Set<String> settingsToPreserve,
+ String settingsKey) {
if (DEBUG) {
Log.i(TAG, "restoreSettings: " + contentUri);
}
- SettingsBackupWhitelist whitelist = getBackupWhitelist(contentUri);
+ SettingsBackupAllowlist allowlist = getBackupAllowlist(contentUri);
// Restore only the white list data.
final ArrayMap<String, String> cachedEntries = new ArrayMap<>();
@@ -850,7 +905,8 @@
Set<String> blockedSettings = getBlockedSettings(blockedSettingsArrayId);
- for (String key : whitelist.mSettingsWhitelist) {
+ int restoredSettingsCount = 0;
+ for (String key : allowlist.mSettingsAllowlist) {
boolean isBlockedBySystem = blockedSettings != null && blockedSettings.contains(key);
if (isBlockedBySystem || isBlockedByDynamicList(dynamicBlockList, contentUri, key)) {
Log.i(
@@ -860,6 +916,12 @@
+ " removed from restore by "
+ (isBlockedBySystem ? "system" : "dynamic")
+ " block list");
+ if (areAgentMetricsEnabled) {
+ mBackupRestoreEventLogger.logItemsRestoreFailed(
+ settingsKey,
+ /* count= */ 1,
+ isBlockedBySystem ? ERROR_SKIPPED_BY_SYSTEM : ERROR_SKIPPED_BY_BLOCKLIST);
+ }
continue;
}
@@ -870,12 +932,20 @@
if (isSettingPreserved && !Settings.Secure.NAVIGATION_MODE.equals(key)) {
Log.i(TAG, "Skipping restore for setting " + key + " as it is marked as "
+ "preserved");
+ if (areAgentMetricsEnabled) {
+ mBackupRestoreEventLogger.logItemsRestoreFailed(
+ settingsKey, /* count= */ 1, ERROR_SKIPPED_PRESERVED);
+ }
continue;
}
if (LargeScreenSettings.doNotRestoreIfLargeScreenSetting(key, getBaseContext())) {
Log.i(TAG, "Skipping restore for setting " + key + " as the target device "
+ "is a large screen (i.e tablet or foldable in unfolded state)");
+ if (areAgentMetricsEnabled) {
+ mBackupRestoreEventLogger.logItemsRestoreFailed(
+ settingsKey, /* count= */ 1, ERROR_SKIPPED_DUE_TO_LARGE_SCREEN);
+ }
continue;
}
@@ -912,19 +982,34 @@
}
// only restore the settings that have valid values
- if (!isValidSettingValue(key, value, whitelist.mSettingsValidators)) {
+ if (!isValidSettingValue(key, value, allowlist.mSettingsValidators)) {
Log.w(TAG, "Attempted restore of " + key + " setting, but its value didn't pass"
+ " validation, value: " + value);
+ if (areAgentMetricsEnabled) {
+ mBackupRestoreEventLogger.logItemsRestoreFailed(
+ settingsKey, /* count= */ 1, ERROR_DID_NOT_PASS_VALIDATION);
+ }
continue;
}
final Uri destination;
+ // If the destination changes, we need to update the key used as datatype for metrics.
+ String finalSettingsKey = settingsKey;
if (movedToGlobal != null && movedToGlobal.contains(key)) {
destination = Settings.Global.CONTENT_URI;
+ if (areAgentMetricsEnabled) {
+ finalSettingsKey = KEY_GLOBAL;
+ }
} else if (movedToSecure != null && movedToSecure.contains(key)) {
destination = Settings.Secure.CONTENT_URI;
+ if (areAgentMetricsEnabled) {
+ finalSettingsKey = KEY_SECURE;
+ }
} else if (movedToSystem != null && movedToSystem.contains(key)) {
destination = Settings.System.CONTENT_URI;
+ if (areAgentMetricsEnabled) {
+ finalSettingsKey = KEY_SYSTEM;
+ }
} else {
destination = contentUri;
}
@@ -942,6 +1027,10 @@
if (isSettingPreserved) {
Log.i(TAG, "Skipping restore for setting navigation_mode "
+ "as it is marked as preserved");
+ if (areAgentMetricsEnabled) {
+ mBackupRestoreEventLogger.logItemsRestoreFailed(
+ finalSettingsKey, /* count= */ 1, ERROR_SKIPPED_PRESERVED);
+ }
continue;
}
}
@@ -961,12 +1050,16 @@
Log.d(TAG, "Restored font scale from: " + toRestore + " to " + value);
}
-
+ // TODO(b/379861078): Log metrics inside this method.
settingsHelper.restoreValue(this, cr, contentValues, destination, key, value,
mRestoredFromSdkInt);
Log.d(TAG, "Restored setting: " + destination + " : " + key + "=" + value);
+ if (areAgentMetricsEnabled) {
+ mBackupRestoreEventLogger.logItemsRestored(finalSettingsKey, /* count= */ 1);
+ }
}
+
}
@@ -996,29 +1089,29 @@
}
@VisibleForTesting
- SettingsBackupWhitelist getBackupWhitelist(Uri contentUri) {
+ SettingsBackupAllowlist getBackupAllowlist(Uri contentUri) {
// Figure out the white list and redirects to the global table. We restore anything
// in either the backup allowlist or the legacy-restore allowlist for this table.
- String[] whitelist;
+ String[] allowlist;
Map<String, Validator> validators = null;
if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
- whitelist = ArrayUtils.concat(String.class, SecureSettings.SETTINGS_TO_BACKUP,
+ allowlist = ArrayUtils.concat(String.class, SecureSettings.SETTINGS_TO_BACKUP,
Settings.Secure.LEGACY_RESTORE_SETTINGS,
DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
validators = SecureSettingsValidators.VALIDATORS;
} else if (contentUri.equals(Settings.System.CONTENT_URI)) {
- whitelist = ArrayUtils.concat(String.class, SystemSettings.SETTINGS_TO_BACKUP,
+ allowlist = ArrayUtils.concat(String.class, SystemSettings.SETTINGS_TO_BACKUP,
Settings.System.LEGACY_RESTORE_SETTINGS);
validators = SystemSettingsValidators.VALIDATORS;
} else if (contentUri.equals(Settings.Global.CONTENT_URI)) {
- whitelist = ArrayUtils.concat(String.class, getGlobalSettingsToBackup(),
+ allowlist = ArrayUtils.concat(String.class, getGlobalSettingsToBackup(),
Settings.Global.LEGACY_RESTORE_SETTINGS);
validators = GlobalSettingsValidators.VALIDATORS;
} else {
throw new IllegalArgumentException("Unknown URI: " + contentUri);
}
- return new SettingsBackupWhitelist(whitelist, validators);
+ return new SettingsBackupAllowlist(allowlist, validators);
}
private String[] getGlobalSettingsToBackup() {
@@ -1118,11 +1211,20 @@
*
* @param cursor A cursor with settings data.
* @param settings The settings to extract.
+ * @param settingsKey The key of the settings to extract (eg system).
* @return The byte array of extracted values.
*/
- private byte[] extractRelevantValues(Cursor cursor, String[] settings) {
+ private byte[] extractRelevantValues(
+ Cursor cursor, String[] settings, String settingsKey) {
if (!cursor.moveToFirst()) {
Log.e(TAG, "Couldn't read from the cursor");
+ if (areAgentMetricsEnabled) {
+ mBackupRestoreEventLogger
+ .logItemsBackupFailed(
+ settingsKey,
+ settings.length,
+ ERROR_COULD_NOT_READ_FROM_CURSOR);
+ }
return new byte[0];
}
@@ -1181,6 +1283,10 @@
}
}
+ if (areAgentMetricsEnabled) {
+ numberOfSettingsPerKey.put(settingsKey, backedUpSettingIndex);
+ }
+
// Aggregate the result.
byte[] result = new byte[totalSize];
int pos = 0;
@@ -1364,7 +1470,9 @@
getContentResolver()
.query(Settings.Secure.CONTENT_URI, PROJECTION, null, null, null)) {
return extractRelevantValues(
- cursor, DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
+ cursor,
+ DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP,
+ KEY_DEVICE_SPECIFIC_CONFIG);
}
}
@@ -1399,7 +1507,8 @@
null,
blockedSettingsArrayId,
dynamicBlocklist,
- preservedSettings);
+ preservedSettings,
+ KEY_DEVICE_SPECIFIC_CONFIG);
updateWindowManagerIfNeeded(originalDensity);
@@ -1597,14 +1706,14 @@
* Store the allowlist of settings to be backed up and validators for them.
*/
@VisibleForTesting
- static class SettingsBackupWhitelist {
- final String[] mSettingsWhitelist;
+ static class SettingsBackupAllowlist {
+ final String[] mSettingsAllowlist;
final Map<String, Validator> mSettingsValidators;
- SettingsBackupWhitelist(String[] settingsWhitelist,
+ SettingsBackupAllowlist(String[] settingsAllowlist,
Map<String, Validator> settingsValidators) {
- mSettingsWhitelist = settingsWhitelist;
+ mSettingsAllowlist = settingsAllowlist;
mSettingsValidators = settingsValidators;
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index c0e61ee..7aed615 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -409,6 +409,11 @@
Slog.w(LOG_TAG, "Bulk sync request to acongid failed.");
}
}
+
+ if (Flags.disableBulkCompare()) {
+ return;
+ }
+
// TOBO(b/312444587): remove the comparison logic after Test Mission 2.
if (requests == null) {
Map<String, AconfigdFlagInfo> aconfigdFlagMap =
@@ -421,7 +426,7 @@
}
}
- // TOBO(b/312444587): remove the comparison logic after Test Mission 2.
+ // TODO(b/312444587): remove the comparison logic after Test Mission 2.
public int compareFlagValueInNewStorage(
Map<String, AconfigdFlagInfo> defaultFlagMap,
Map<String, AconfigdFlagInfo> aconfigdFlagMap) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
index aca26ec..cfd27c6 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
+++ b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
@@ -101,3 +101,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "disable_bulk_compare"
+ namespace: "core_experiments_team_internal"
+ description: "Disable bulk comparison between DeviceConfig and aconfig storage."
+ bug: "312444587"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
index 3a391505..350c149 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
@@ -17,11 +17,23 @@
package com.android.providers.settings;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertArrayEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+import android.app.backup.BackupAnnotations.BackupDestination;
+import android.app.backup.BackupAnnotations.OperationType;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupRestoreEventLogger;
+import android.app.backup.BackupRestoreEventLogger.DataTypeResult;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
@@ -32,7 +44,10 @@
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.os.UserHandle;
+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.provider.settings.validators.SettingsValidators;
import android.provider.settings.validators.Validator;
@@ -43,9 +58,14 @@
import com.android.window.flags.Flags;
+import java.util.List;
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.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -54,12 +74,14 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
+
/**
* Tests for the SettingsHelperTest
* Usage: atest SettingsProviderTest:SettingsBackupAgentTest
@@ -73,6 +95,17 @@
private static final Map<String, String> DEVICE_SPECIFIC_TEST_VALUES = new HashMap<>();
private static final Map<String, String> TEST_VALUES = new HashMap<>();
private static final Map<String, Validator> TEST_VALUES_VALIDATORS = new HashMap<>();
+ private static final String TEST_KEY = "test_key";
+ private static final String TEST_VALUE = "test_value";
+ private static final String ERROR_COULD_NOT_READ_ENTITY = "could_not_read_entity";
+ private static final String ERROR_SKIPPED_BY_SYSTEM = "skipped_by_system";
+ private static final String ERROR_SKIPPED_BY_BLOCKLIST =
+ "skipped_by_dynamic_blocklist";
+ private static final String ERROR_SKIPPED_PRESERVED = "skipped_preserved";
+ private static final String ERROR_DID_NOT_PASS_VALIDATION = "did_not_pass_validation";
+ private static final String KEY_SYSTEM = "system";
+ private static final String KEY_SECURE = "secure";
+ private static final String KEY_GLOBAL = "global";
static {
DEVICE_SPECIFIC_TEST_VALUES.put(Settings.Secure.DISPLAY_DENSITY_FORCED,
@@ -86,6 +119,14 @@
TEST_VALUES_VALIDATORS.put(PRESERVED_TEST_SETTING, SettingsValidators.ANY_STRING_VALIDATOR);
}
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock private BackupDataInput mBackupDataInput;
+ @Mock private BackupDataOutput mBackupDataOutput;
+
private TestFriendlySettingsBackupAgent mAgentUnderTest;
private Context mContext;
@@ -203,19 +244,32 @@
@Test
public void testOnRestore_preservedSettingsAreNotRestored() {
- SettingsBackupAgent.SettingsBackupWhitelist whitelist =
- new SettingsBackupAgent.SettingsBackupWhitelist(
+ SettingsBackupAgent.SettingsBackupAllowlist allowlist =
+ new SettingsBackupAgent.SettingsBackupAllowlist(
new String[] { OVERRIDDEN_TEST_SETTING, PRESERVED_TEST_SETTING },
TEST_VALUES_VALIDATORS);
- mAgentUnderTest.setSettingsWhitelist(whitelist);
+ mAgentUnderTest.setSettingsAllowlist(allowlist);
mAgentUnderTest.setBlockedSettings();
TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
mAgentUnderTest.mSettingsHelper = settingsHelper;
byte[] backupData = generateBackupData(TEST_VALUES);
- mAgentUnderTest.restoreSettings(backupData, /* pos */ 0, backupData.length, TEST_URI,
- null, null, null, /* blockedSettingsArrayId */ 0, Collections.emptySet(),
- new HashSet<>(Collections.singletonList(SettingsBackupAgent.getQualifiedKeyForSetting(PRESERVED_TEST_SETTING, TEST_URI))));
+ mAgentUnderTest.restoreSettings(
+ backupData,
+ /* pos */ 0,
+ backupData.length,
+ TEST_URI,
+ null,
+ null,
+ null,
+ /* blockedSettingsArrayId */ 0,
+ Collections.emptySet(),
+ new HashSet<>(Collections
+ .singletonList(
+ SettingsBackupAgent
+ .getQualifiedKeyForSetting(
+ PRESERVED_TEST_SETTING, TEST_URI))),
+ TEST_KEY);
assertTrue(settingsHelper.mWrittenValues.containsKey(OVERRIDDEN_TEST_SETTING));
assertFalse(settingsHelper.mWrittenValues.containsKey(PRESERVED_TEST_SETTING));
@@ -262,6 +316,486 @@
assertEquals("1.5", testedMethod.apply("1.8"));
}
+ @Test
+ @DisableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void onCreate_metricsFlagIsDisabled_areAgentMetricsEnabledIsFalse() {
+ mAgentUnderTest.onCreate();
+
+ assertFalse(mAgentUnderTest.areAgentMetricsEnabled);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void onCreate_flagIsEnabled_areAgentMetricsEnabledIsTrue() {
+ mAgentUnderTest.onCreate();
+
+ assertTrue(mAgentUnderTest.areAgentMetricsEnabled);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void writeDataForKey_metricsFlagIsEnabled_numberOfSettingsPerKeyContainsKey_dataWriteSucceeds_logsSuccessMetrics()
+ throws IOException {
+ when(mBackupDataOutput.writeEntityHeader(anyString(), anyInt())).thenReturn(0);
+ when(mBackupDataOutput.writeEntityData(any(byte[].class), anyInt())).thenReturn(0);
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP);
+ mAgentUnderTest.setNumberOfSettingsPerKey(TEST_KEY, 1);
+
+ mAgentUnderTest.writeDataForKey(
+ TEST_KEY, TEST_VALUE.getBytes(), mBackupDataOutput);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getSuccessCount(), 1);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void writeDataForKey_metricsFlagIsEnabled_numberOfSettingsPerKeyContainsKey_writeEntityHeaderFails_logsFailureMetrics()
+ throws IOException {
+ when(mBackupDataOutput.writeEntityHeader(anyString(), anyInt())).thenThrow(new IOException());
+ when(mBackupDataOutput.writeEntityData(any(byte[].class), anyInt())).thenReturn(0);
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP);
+ mAgentUnderTest.setNumberOfSettingsPerKey(TEST_KEY, 1);
+
+ mAgentUnderTest.writeDataForKey(
+ TEST_KEY, TEST_VALUE.getBytes(), mBackupDataOutput);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getFailCount(), 1);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void writeDataForKey_metricsFlagIsEnabled_numberOfSettingsPerKeyContainsKey_writeEntityDataFails_logsFailureMetrics()
+ throws IOException {
+ when(mBackupDataOutput.writeEntityHeader(anyString(), anyInt())).thenReturn(0);
+ when(mBackupDataOutput.writeEntityData(any(byte[].class), anyInt())).thenThrow(new IOException());
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP);
+ mAgentUnderTest.setNumberOfSettingsPerKey(TEST_KEY, 1);
+
+ mAgentUnderTest.writeDataForKey(
+ TEST_KEY, TEST_VALUE.getBytes(), mBackupDataOutput);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getFailCount(), 1);
+ }
+
+ @Test
+ @DisableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void writeDataForKey_metricsFlagIsDisabled_doesNotLogMetrics()
+ throws IOException {
+ when(mBackupDataOutput.writeEntityHeader(anyString(), anyInt())).thenReturn(0);
+ when(mBackupDataOutput.writeEntityData(any(byte[].class), anyInt())).thenReturn(0);
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP);
+ mAgentUnderTest.setNumberOfSettingsPerKey(TEST_KEY, 1);
+
+ mAgentUnderTest.writeDataForKey(
+ TEST_KEY, TEST_VALUE.getBytes(), mBackupDataOutput);
+
+ assertNull(getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest));
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void writeDataForKey_metricsFlagIsEnabled_numberOfSettingsPerKeyDoesNotContainKey_doesNotLogMetrics()
+ throws IOException {
+ when(mBackupDataOutput.writeEntityHeader(anyString(), anyInt())).thenReturn(0);
+ when(mBackupDataOutput.writeEntityData(any(byte[].class), anyInt())).thenReturn(0);
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP);
+
+ mAgentUnderTest.writeDataForKey(
+ TEST_KEY, TEST_VALUE.getBytes(), mBackupDataOutput);
+
+ assertNull(getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest));
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreEnabled_agentMetricsAreLogged() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ SettingsBackupAgent.SettingsBackupAllowlist allowlist =
+ new SettingsBackupAgent.SettingsBackupAllowlist(
+ new String[] {OVERRIDDEN_TEST_SETTING},
+ TEST_VALUES_VALIDATORS);
+ mAgentUnderTest.setSettingsAllowlist(allowlist);
+ mAgentUnderTest.setBlockedSettings();
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+ byte[] backupData = generateBackupData(TEST_VALUES);
+ mAgentUnderTest
+ .restoreSettings(
+ backupData,
+ /* pos= */ 0,
+ backupData.length,
+ TEST_URI,
+ /* movedToGlobal= */ null,
+ /* movedToSecure= */ null,
+ /* movedToSystem= */ null,
+ /* blockedSettingsArrayId= */ 0,
+ /* dynamicBlockList= */ Collections.emptySet(),
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getSuccessCount(), 1);
+ }
+
+ @Test
+ @DisableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreDisabled_agentMetricsAreNotLogged() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ SettingsBackupAgent.SettingsBackupAllowlist allowlist =
+ new SettingsBackupAgent.SettingsBackupAllowlist(
+ new String[] {OVERRIDDEN_TEST_SETTING},
+ TEST_VALUES_VALIDATORS);
+ mAgentUnderTest.setSettingsAllowlist(allowlist);
+ mAgentUnderTest.setBlockedSettings();
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+ byte[] backupData = generateBackupData(TEST_VALUES);
+ mAgentUnderTest
+ .restoreSettings(
+ backupData,
+ /* pos= */ 0,
+ backupData.length,
+ TEST_URI,
+ /* movedToGlobal= */ null,
+ /* movedToSecure= */ null,
+ /* movedToSystem= */ null,
+ /* blockedSettingsArrayId= */ 0,
+ /* dynamicBlockList= */ Collections.emptySet(),
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest);
+ assertNull(loggingResult);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreEnabled_readEntityDataFails_failureIsLogged()
+ throws IOException {
+ when(mBackupDataInput.readEntityData(any(byte[].class), anyInt(), anyInt()))
+ .thenThrow(new IOException());
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+
+ mAgentUnderTest.restoreSettings(
+ mBackupDataInput,
+ TEST_URI,
+ /* movedToGlobal= */ null,
+ /* movedToSecure= */ null,
+ /* movedToSystem= */ null,
+ /* blockedSettingsArrayId= */ 0,
+ /* dynamicBlockList= */ Collections.emptySet(),
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getFailCount(), 1);
+ assertTrue(loggingResult.getErrors().containsKey(ERROR_COULD_NOT_READ_ENTITY));
+ }
+
+ @Test
+ @DisableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreDisabled_readEntityDataFails_failureIsNotLogged()
+ throws IOException {
+ when(mBackupDataInput.readEntityData(any(byte[].class), anyInt(), anyInt()))
+ .thenThrow(new IOException());
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+
+ mAgentUnderTest.restoreSettings(
+ mBackupDataInput,
+ TEST_URI,
+ /* movedToGlobal= */ null,
+ /* movedToSecure= */ null,
+ /* movedToSystem= */ null,
+ /* blockedSettingsArrayId= */ 0,
+ /* dynamicBlockList= */ Collections.emptySet(),
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
+
+ assertNull(getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest));
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreEnabled_settingIsSkippedBySystem_failureIsLogged() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ String[] settingBlockedBySystem = new String[] {OVERRIDDEN_TEST_SETTING};
+ SettingsBackupAgent.SettingsBackupAllowlist allowlist =
+ new SettingsBackupAgent.SettingsBackupAllowlist(
+ settingBlockedBySystem,
+ TEST_VALUES_VALIDATORS);
+ mAgentUnderTest.setSettingsAllowlist(allowlist);
+ mAgentUnderTest.setBlockedSettings(settingBlockedBySystem);
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+ byte[] backupData = generateBackupData(TEST_VALUES);
+ mAgentUnderTest
+ .restoreSettings(
+ backupData,
+ /* pos= */ 0,
+ backupData.length,
+ TEST_URI,
+ /* movedToGlobal= */ null,
+ /* movedToSecure= */ null,
+ /* movedToSystem= */ null,
+ /* blockedSettingsArrayId= */ 0,
+ /* dynamicBlockList= */ Collections.emptySet(),
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getFailCount(), 1);
+ assertTrue(loggingResult.getErrors().containsKey(ERROR_SKIPPED_BY_SYSTEM));
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreEnabled_settingIsSkippedByBlockList_failureIsLogged() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ SettingsBackupAgent.SettingsBackupAllowlist allowlist =
+ new SettingsBackupAgent.SettingsBackupAllowlist(
+ new String[] {OVERRIDDEN_TEST_SETTING},
+ TEST_VALUES_VALIDATORS);
+ mAgentUnderTest.setSettingsAllowlist(allowlist);
+ mAgentUnderTest.setBlockedSettings();
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+ Set<String> dynamicBlockList =
+ Set.of(Uri.withAppendedPath(TEST_URI, OVERRIDDEN_TEST_SETTING).toString());
+
+ byte[] backupData = generateBackupData(TEST_VALUES);
+ mAgentUnderTest
+ .restoreSettings(
+ backupData,
+ /* pos= */ 0,
+ backupData.length,
+ TEST_URI,
+ /* movedToGlobal= */ null,
+ /* movedToSecure= */ null,
+ /* movedToSystem= */ null,
+ /* blockedSettingsArrayId= */ 0,
+ dynamicBlockList,
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getFailCount(), 1);
+ assertTrue(loggingResult.getErrors().containsKey(ERROR_SKIPPED_BY_BLOCKLIST));
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreEnabled_settingIsPreserved_failureIsLogged() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ SettingsBackupAgent.SettingsBackupAllowlist allowlist =
+ new SettingsBackupAgent.SettingsBackupAllowlist(
+ new String[] {OVERRIDDEN_TEST_SETTING},
+ TEST_VALUES_VALIDATORS);
+ mAgentUnderTest.setSettingsAllowlist(allowlist);
+ mAgentUnderTest.setBlockedSettings();
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+ Set<String> preservedSettings =
+ Set.of(Uri.withAppendedPath(TEST_URI, OVERRIDDEN_TEST_SETTING).toString());
+
+ byte[] backupData = generateBackupData(TEST_VALUES);
+ mAgentUnderTest
+ .restoreSettings(
+ backupData,
+ /* pos= */ 0,
+ backupData.length,
+ TEST_URI,
+ /* movedToGlobal= */ null,
+ /* movedToSecure= */ null,
+ /* movedToSystem= */ null,
+ /* blockedSettingsArrayId= */ 0,
+ /* dynamicBlockList = */ Collections.emptySet(),
+ preservedSettings,
+ TEST_KEY);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getFailCount(), 1);
+ assertTrue(loggingResult.getErrors().containsKey(ERROR_SKIPPED_PRESERVED));
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreEnabled_settingIsNotValid_failureIsLogged() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ SettingsBackupAgent.SettingsBackupAllowlist allowlist =
+ new SettingsBackupAgent.SettingsBackupAllowlist(
+ new String[] {OVERRIDDEN_TEST_SETTING},
+ /* settingsValidators= */ null);
+ mAgentUnderTest.setSettingsAllowlist(allowlist);
+ mAgentUnderTest.setBlockedSettings();
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+ byte[] backupData = generateBackupData(TEST_VALUES);
+ mAgentUnderTest
+ .restoreSettings(
+ backupData,
+ /* pos= */ 0,
+ backupData.length,
+ TEST_URI,
+ /* movedToGlobal= */ null,
+ /* movedToSecure= */ null,
+ /* movedToSystem= */ null,
+ /* blockedSettingsArrayId= */ 0,
+ /* dynamicBlockList = */ Collections.emptySet(),
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getFailCount(), 1);
+ assertTrue(loggingResult.getErrors().containsKey(ERROR_DID_NOT_PASS_VALIDATION));
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreEnabled_settingIsMarkedAsMovedToGlobal_agentMetricsAreLoggedWithGlobalKey() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ SettingsBackupAgent.SettingsBackupAllowlist allowlist =
+ new SettingsBackupAgent.SettingsBackupAllowlist(
+ new String[] {OVERRIDDEN_TEST_SETTING},
+ TEST_VALUES_VALIDATORS);
+ mAgentUnderTest.setSettingsAllowlist(allowlist);
+ mAgentUnderTest.setBlockedSettings();
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+ byte[] backupData = generateBackupData(TEST_VALUES);
+ mAgentUnderTest
+ .restoreSettings(
+ backupData,
+ /* pos= */ 0,
+ backupData.length,
+ TEST_URI,
+ /* movedToGlobal= */ Set.of(OVERRIDDEN_TEST_SETTING),
+ /* movedToSecure= */ null,
+ /* movedToSystem= */ null,
+ /* blockedSettingsArrayId= */ 0,
+ /* dynamicBlockList= */ Collections.emptySet(),
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(KEY_GLOBAL, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getSuccessCount(), 1);
+ assertNull(getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest));
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreEnabled_settingIsMarkedAsMovedToSecure_agentMetricsAreLoggedWithSecureKey() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ SettingsBackupAgent.SettingsBackupAllowlist allowlist =
+ new SettingsBackupAgent.SettingsBackupAllowlist(
+ new String[] {OVERRIDDEN_TEST_SETTING},
+ TEST_VALUES_VALIDATORS);
+ mAgentUnderTest.setSettingsAllowlist(allowlist);
+ mAgentUnderTest.setBlockedSettings();
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+ byte[] backupData = generateBackupData(TEST_VALUES);
+ mAgentUnderTest
+ .restoreSettings(
+ backupData,
+ /* pos= */ 0,
+ backupData.length,
+ TEST_URI,
+ /* movedToGlobal= */ null,
+ /* movedToSecure= */ Set.of(OVERRIDDEN_TEST_SETTING),
+ /* movedToSystem= */ null,
+ /* blockedSettingsArrayId= */ 0,
+ /* dynamicBlockList= */ Collections.emptySet(),
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(KEY_SECURE, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getSuccessCount(), 1);
+ assertNull(getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest));
+ }
+
+ @Test
+ @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+ public void restoreSettings_agentMetricsAreEnabled_settingIsMarkedAsMovedToSystem_agentMetricsAreLoggedWithSystemKey() {
+ mAgentUnderTest.onCreate(
+ UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+ SettingsBackupAgent.SettingsBackupAllowlist allowlist =
+ new SettingsBackupAgent.SettingsBackupAllowlist(
+ new String[] {OVERRIDDEN_TEST_SETTING},
+ TEST_VALUES_VALIDATORS);
+ mAgentUnderTest.setSettingsAllowlist(allowlist);
+ mAgentUnderTest.setBlockedSettings();
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+ byte[] backupData = generateBackupData(TEST_VALUES);
+ mAgentUnderTest
+ .restoreSettings(
+ backupData,
+ /* pos= */ 0,
+ backupData.length,
+ TEST_URI,
+ /* movedToGlobal= */ null,
+ /* movedToSecure= */ null,
+ /* movedToSystem= */ Set.of(OVERRIDDEN_TEST_SETTING),
+ /* blockedSettingsArrayId= */ 0,
+ /* dynamicBlockList= */ Collections.emptySet(),
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
+
+ DataTypeResult loggingResult =
+ getLoggingResultForDatatype(KEY_SYSTEM, mAgentUnderTest);
+ assertNotNull(loggingResult);
+ assertEquals(loggingResult.getSuccessCount(), 1);
+ assertNull(getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest));
+ }
+
private byte[] generateBackupData(Map<String, String> keyValueData) {
int totalBytes = 0;
for (String key : keyValueData.keySet()) {
@@ -293,7 +827,8 @@
null,
R.array.restore_blocked_global_settings,
/* dynamicBlockList= */ Collections.emptySet(),
- /* settingsToPreserve= */ Collections.emptySet());
+ /* settingsToPreserve= */ Collections.emptySet(),
+ TEST_KEY);
}
private byte[] generateUncorruptedHeader() throws IOException {
@@ -329,6 +864,21 @@
}
}
+ private DataTypeResult getLoggingResultForDatatype(
+ String dataType, SettingsBackupAgent agent) {
+ if (agent.getBackupRestoreEventLogger() == null) {
+ return null;
+ }
+ List<DataTypeResult> loggingResults =
+ agent.getBackupRestoreEventLogger().getLoggingResults();
+ for (DataTypeResult result : loggingResults) {
+ if (result.getDataType().equals(dataType)) {
+ return result;
+ }
+ }
+ return null;
+ }
+
private byte[] generateSingleKeyTestBackupData(String key, String value) throws IOException {
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
os.write(SettingsBackupAgent.toByteArray(key));
@@ -340,7 +890,7 @@
private static class TestFriendlySettingsBackupAgent extends SettingsBackupAgent {
private Boolean mForcedDeviceInfoRestoreAcceptability = null;
private String[] mBlockedSettings = null;
- private SettingsBackupWhitelist mSettingsWhitelist = null;
+ private SettingsBackupAllowlist mSettingsAllowlist = null;
void setForcedDeviceInfoRestoreAcceptability(boolean value) {
mForcedDeviceInfoRestoreAcceptability = value;
@@ -350,8 +900,8 @@
mBlockedSettings = blockedSettings;
}
- void setSettingsWhitelist(SettingsBackupWhitelist settingsWhitelist) {
- mSettingsWhitelist = settingsWhitelist;
+ void setSettingsAllowlist(SettingsBackupAllowlist settingsAllowlist) {
+ mSettingsAllowlist = settingsAllowlist;
}
@Override
@@ -369,12 +919,18 @@
}
@Override
- SettingsBackupWhitelist getBackupWhitelist(Uri contentUri) {
- if (mSettingsWhitelist == null) {
- return super.getBackupWhitelist(contentUri);
+ SettingsBackupAllowlist getBackupAllowlist(Uri contentUri) {
+ if (mSettingsAllowlist == null) {
+ return super.getBackupAllowlist(contentUri);
}
- return mSettingsWhitelist;
+ return mSettingsAllowlist;
+ }
+
+ void setNumberOfSettingsPerKey(String key, int numberOfSettings) {
+ if (numberOfSettingsPerKey != null) {
+ this.numberOfSettingsPerKey.put(key, numberOfSettings);
+ }
}
}
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index 48ce49d..276b206 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -27,6 +27,7 @@
import android.aconfigd.AconfigdFlagInfo;
import android.os.Looper;
import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.Xml;
@@ -1304,6 +1305,7 @@
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_DISABLE_BULK_COMPARE)
public void testCompareFlagValueInNewStorage() {
int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
Object lock = new Object();
diff --git a/packages/Shell/Android.bp b/packages/Shell/Android.bp
index 5f81085..5fdf045 100644
--- a/packages/Shell/Android.bp
+++ b/packages/Shell/Android.bp
@@ -12,7 +12,10 @@
"src/**/*.java",
":dumpstate_aidl",
]
-shell_static_libs = ["androidx.legacy_legacy-support-v4"]
+shell_static_libs = [
+ "androidx.legacy_legacy-support-v4",
+ "wear_aconfig_declarations_flags_java_lib",
+]
android_app {
name: "Shell",
@@ -28,6 +31,7 @@
flags_packages: [
"android.security.flags-aconfig",
"android.permission.flags-aconfig",
+ "wear_aconfig_declarations",
],
platform_apis: true,
certificate: "platform",
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index fa6e2db..65f4877 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -989,6 +989,10 @@
<uses-permission android:name="android.permission.health.READ_SKIN_TEMPERATURE"
android:featureFlag="android.permission.flags.replace_body_sensor_permission_enabled"/>
+ <!-- Permission for TestClassifier tests to get access to classifier by type -->
+ <uses-permission android:name="android.permission.ACCESS_TEXT_CLASSIFIER_BY_TYPE"
+ android:featureFlag="android.permission.flags.text_classifier_choice_api_enabled"/>
+
<application
android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/Shell/res/values/defaults.xml b/packages/Shell/res/values/defaults.xml
new file mode 100644
index 0000000..b693cc8
--- /dev/null
+++ b/packages/Shell/res/values/defaults.xml
@@ -0,0 +1,5 @@
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Default for Wear bugreport warning activity-->
+ <!-- DO NOT TRANSLATE -->
+ <string name="system_ui_wear_bugreport_warning_activity" />
+</resources>
\ No newline at end of file
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 7f25b51..0694b61 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -24,6 +24,7 @@
import static com.android.shell.BugreportPrefs.STATE_HIDE;
import static com.android.shell.BugreportPrefs.STATE_UNKNOWN;
import static com.android.shell.BugreportPrefs.getWarningState;
+import static com.android.shell.flags.Flags.handleBugreportsForWear;
import android.accounts.Account;
import android.accounts.AccountManager;
@@ -89,10 +90,10 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.google.android.collect.Lists;
-
import libcore.io.Streams;
+import com.google.android.collect.Lists;
+
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -140,6 +141,7 @@
public class BugreportProgressService extends Service {
private static final String TAG = "BugreportProgressService";
private static final boolean DEBUG = false;
+ private static final String WRITE_AND_APPEND_MODE = "wa";
private Intent startSelfIntent;
@@ -384,7 +386,11 @@
}
private static String getFileName(BugreportInfo info, String suffix) {
- return String.format("%s-%s%s", info.baseName, info.getName(), suffix);
+ return getFileName(suffix, info.baseName, info.getName());
+ }
+
+ private static String getFileName(String suffix, String baseName, String name) {
+ return String.format("%s-%s%s", baseName, name, suffix);
}
private final class BugreportCallbackImpl extends BugreportCallback {
@@ -420,14 +426,14 @@
@Override
public void onFinished() {
- mInfo.renameBugreportFile();
- mInfo.renameScreenshots();
- if (mInfo.bugreportFile.length() == 0) {
- Log.e(TAG, "Bugreport file empty. File path = " + mInfo.bugreportFile);
- onError(BUGREPORT_ERROR_RUNTIME);
- return;
- }
synchronized (mLock) {
+ mInfo.renameBugreportFile();
+ mInfo.renameScreenshots();
+ if (mInfo.bugreportLocationInfo.isFileEmpty(mContext)) {
+ Log.e(TAG, "Bugreport file empty. File path = " + mInfo.bugreportLocationInfo);
+ onError(BUGREPORT_ERROR_RUNTIME);
+ return;
+ }
sendBugreportFinishedBroadcastLocked();
mMainThreadHandler.post(() -> mInfoDialog.onBugreportFinished(mInfo));
}
@@ -454,15 +460,15 @@
@GuardedBy("mLock")
private void sendBugreportFinishedBroadcastLocked() {
- final String bugreportFilePath = mInfo.bugreportFile.getAbsolutePath();
- if (mInfo.type == BugreportParams.BUGREPORT_MODE_REMOTE) {
- sendRemoteBugreportFinishedBroadcast(mContext, bugreportFilePath,
- mInfo.bugreportFile, mInfo.nonce);
+ File bugreportFile = mInfo.bugreportLocationInfo.mBugreportFile;
+ if (mInfo.type == BugreportParams.BUGREPORT_MODE_REMOTE && bugreportFile != null) {
+ sendRemoteBugreportFinishedBroadcast(
+ mContext, bugreportFile.getAbsolutePath(), bugreportFile, mInfo.nonce);
} else {
cleanupOldFiles(MIN_KEEP_COUNT, MIN_KEEP_AGE, mBugreportsDir);
final Intent intent = new Intent(INTENT_BUGREPORT_FINISHED);
- intent.putExtra(EXTRA_BUGREPORT, bugreportFilePath);
- intent.putExtra(EXTRA_SCREENSHOT, getScreenshotForIntent(mInfo));
+ intent.putExtra(EXTRA_BUGREPORT, mInfo.bugreportLocationInfo.getBugreportPath());
+ intent.putExtra(EXTRA_SCREENSHOT, mInfo.screenshotLocationInfo.getScreenshotPath());
mContext.sendBroadcast(intent, android.Manifest.permission.DUMP);
onBugreportFinished(mInfo);
}
@@ -498,19 +504,6 @@
android.Manifest.permission.DUMP);
}
- /**
- * Checks if screenshot array is non-empty and returns the first screenshot's path. The first
- * screenshot is the default screenshot for the bugreport types that take it.
- */
- private static String getScreenshotForIntent(BugreportInfo info) {
- if (!info.screenshotFiles.isEmpty()) {
- final File screenshotFile = info.screenshotFiles.get(0);
- final String screenshotFilePath = screenshotFile.getAbsolutePath();
- return screenshotFilePath;
- }
- return null;
- }
-
private static String generateFileHash(String fileName) {
String fileHash = null;
try {
@@ -715,24 +708,28 @@
String name = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
List<Uri> extraAttachments =
intent.getParcelableArrayListExtra(EXTRA_EXTRA_ATTACHMENT_URIS, Uri.class);
-
- BugreportInfo info = new BugreportInfo(mContext, baseName, name, shareTitle,
- shareDescription, bugreportType, mBugreportsDir, nonce, extraAttachments);
- synchronized (mLock) {
- if (info.bugreportFile.exists()) {
- Log.e(TAG, "Failed to start bugreport generation, the requested bugreport file "
- + info.bugreportFile + " already exists");
- return;
- }
- info.createBugreportFile();
+ BugreportInfo info =
+ setupFilesAndCreateBugreportInfo(
+ intent,
+ bugreportType,
+ baseName,
+ name,
+ shareTitle,
+ shareDescription,
+ nonce,
+ extraAttachments);
+ if (info == null) {
+ Log.e(TAG, "Could not initialize bugreport inputs");
+ return;
}
+
ParcelFileDescriptor bugreportFd = info.getBugreportFd();
if (bugreportFd == null) {
Log.e(TAG, "Failed to start bugreport generation as "
+ " bugreport parcel file descriptor is null.");
return;
}
- info.createScreenshotFile(mBugreportsDir);
+
ParcelFileDescriptor screenshotFd = null;
if (isDefaultScreenshotRequired(bugreportType, /* hasScreenshotButton= */ !mIsTv)) {
screenshotFd = info.getDefaultScreenshotFd();
@@ -740,7 +737,7 @@
Log.e(TAG, "Failed to start bugreport generation as"
+ " screenshot parcel file descriptor is null. Deleting bugreport file");
FileUtils.closeQuietly(bugreportFd);
- info.bugreportFile.delete();
+ info.bugreportLocationInfo.maybeDeleteBugreportFile();
return;
}
}
@@ -768,6 +765,56 @@
}
}
+ // Sets up BugreportInfo. If needed, creates bugreport and screenshot files.
+ private BugreportInfo setupFilesAndCreateBugreportInfo(
+ Intent intent,
+ int bugreportType,
+ String baseName,
+ String name,
+ String shareTitle,
+ String shareDescription,
+ long nonce,
+ List<Uri> extraAttachments) {
+ ArrayList<Uri> brAndScreenshot;
+ Uri bugReportUri = null;
+ Uri screenshotUri = null;
+
+ if (handleBugreportsForWear() && bugreportType == BugreportParams.BUGREPORT_MODE_WEAR) {
+ brAndScreenshot = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
+ if (brAndScreenshot != null && !brAndScreenshot.isEmpty()) {
+ bugReportUri = brAndScreenshot.get(0);
+ if (bugReportUri == null) {
+ Log.e(TAG, "Can't start bugreport request. Bugreport uri is null.");
+ return null;
+ }
+ screenshotUri = (brAndScreenshot.size() > 1) ? brAndScreenshot.get(1) : null;
+ }
+ }
+
+ BugreportLocationInfo bugreportLocationInfo =
+ new BugreportLocationInfo(bugReportUri, mBugreportsDir, baseName, name);
+ ScreenshotLocationInfo screenshotLocationInfo = new ScreenshotLocationInfo(screenshotUri);
+ BugreportInfo info =
+ new BugreportInfo(
+ mContext,
+ baseName,
+ name,
+ shareTitle,
+ shareDescription,
+ bugreportType,
+ nonce,
+ extraAttachments,
+ bugreportLocationInfo,
+ screenshotLocationInfo);
+ synchronized (mLock) {
+ if (!bugreportLocationInfo.maybeCreateBugreportFile()) {
+ return null;
+ }
+ }
+ info.maybeCreateScreenshotFile(mBugreportsDir);
+ return info;
+ }
+
private static boolean isDefaultScreenshotRequired(
@BugreportParams.BugreportMode int bugreportType,
boolean hasScreenshotButton) {
@@ -1177,8 +1224,9 @@
stopForegroundWhenDoneLocked(info.id);
}
- if (!info.bugreportFile.exists() || !info.bugreportFile.canRead()) {
- Log.e(TAG, "Could not read bugreport file " + info.bugreportFile);
+ File bugreportFile = info.bugreportLocationInfo.mBugreportFile;
+ if (!info.bugreportLocationInfo.isValidBugreportResult()) {
+ Log.e(TAG, "Could not read bugreport file " + bugreportFile);
Toast.makeText(mContext, R.string.bugreport_unreadable_text, Toast.LENGTH_LONG).show();
synchronized (mLock) {
stopProgressLocked(info.id);
@@ -1194,7 +1242,7 @@
* the bugreport.
*/
private void triggerLocalNotification(final BugreportInfo info) {
- boolean isPlainText = info.bugreportFile.getName().toLowerCase().endsWith(".txt");
+ boolean isPlainText = info.bugreportLocationInfo.isPlainText();
if (!isPlainText) {
// Already zipped, send it right away.
sendBugreportNotification(info, mTakingScreenshot);
@@ -1223,11 +1271,11 @@
// grant temporary permissions for.
final Uri bugreportUri;
try {
- bugreportUri = getUri(context, info.bugreportFile);
+ bugreportUri = getUri(context, info.bugreportLocationInfo.mBugreportFile);
} catch (IllegalArgumentException e) {
// Should not happen on production, but happens when a Shell is sideloaded and
// FileProvider cannot find a configured root for it.
- Log.wtf(TAG, "Could not get URI for " + info.bugreportFile, e);
+ Log.wtf(TAG, "Could not get URI for " + info.bugreportLocationInfo.mBugreportFile, e);
return null;
}
@@ -1258,7 +1306,7 @@
new ClipData.Item(null, null, null, bugreportUri));
Log.d(TAG, "share intent: bureportUri=" + bugreportUri);
final ArrayList<Uri> attachments = Lists.newArrayList(bugreportUri);
- for (File screenshot : info.screenshotFiles) {
+ for (File screenshot : info.screenshotLocationInfo.mScreenshotFiles) {
final Uri screenshotUri = getUri(context, screenshot);
Log.d(TAG, "share intent: screenshotUri=" + screenshotUri);
clipData.addItem(new ClipData.Item(null, null, null, screenshotUri));
@@ -1317,7 +1365,11 @@
*/
private Intent buildWearWarningIntent() {
Intent intent = new Intent();
- intent.setClassName(mContext, getPackageName() + ".WearBugreportWarningActivity");
+ String systemUIPackage = mContext.getResources().getString(
+ com.android.internal.R.string.config_systemUi);
+ String wearBugreportWarningActivity = getResources()
+ .getString(R.string.system_ui_wear_bugreport_warning_activity);
+ intent.setClassName(systemUIPackage, wearBugreportWarningActivity);
if (mContext.getPackageManager().resolveActivity(intent, /* flags */ 0) == null) {
Log.e(TAG, "Cannot find wear bugreport warning activity");
return buildWarningIntent(mContext, /* sendIntent */ null);
@@ -1512,22 +1564,25 @@
* original in case of failure).
*/
private static void zipBugreport(BugreportInfo info) {
- final String bugreportPath = info.bugreportFile.getAbsolutePath();
+ File bugreportFile = info.bugreportLocationInfo.mBugreportFile;
+ final String bugreportPath = bugreportFile.getAbsolutePath();
final String zippedPath = bugreportPath.replace(".txt", ".zip");
Log.v(TAG, "zipping " + bugreportPath + " as " + zippedPath);
final File bugreportZippedFile = new File(zippedPath);
- try (InputStream is = new FileInputStream(info.bugreportFile);
- ZipOutputStream zos = new ZipOutputStream(
- new BufferedOutputStream(new FileOutputStream(bugreportZippedFile)))) {
- addEntry(zos, info.bugreportFile.getName(), is);
+ try (InputStream is = new FileInputStream(bugreportFile);
+ ZipOutputStream zos =
+ new ZipOutputStream(
+ new BufferedOutputStream(
+ new FileOutputStream(bugreportZippedFile)))) {
+ addEntry(zos, bugreportFile.getName(), is);
// Delete old file
- final boolean deleted = info.bugreportFile.delete();
+ final boolean deleted = bugreportFile.delete();
if (deleted) {
Log.v(TAG, "deleted original bugreport (" + bugreportPath + ")");
} else {
Log.e(TAG, "could not delete original bugreport (" + bugreportPath + ")");
}
- info.bugreportFile = bugreportZippedFile;
+ info.bugreportLocationInfo.mBugreportFile = bugreportZippedFile;
} catch (IOException e) {
Log.e(TAG, "exception zipping file " + zippedPath, e);
}
@@ -1557,7 +1612,11 @@
@GuardedBy("mLock")
private void addDetailsToZipFileLocked(BugreportInfo info) {
- if (info.bugreportFile == null) {
+ if (handleBugreportsForWear()) {
+ Log.d(TAG, "Skipping adding details to zipped file");
+ return;
+ }
+ if (info.bugreportLocationInfo.mBugreportFile == null) {
// One possible reason is a bug in the Parcelization code.
Log.wtf(TAG, "addDetailsToZipFile(): no bugreportFile on " + info);
return;
@@ -1588,10 +1647,11 @@
sendBugreportBeingUpdatedNotification(mContext, info.id); // ...and that takes time
}
- final File dir = info.bugreportFile.getParentFile();
- final File tmpZip = new File(dir, "tmp-" + info.bugreportFile.getName());
+ File bugreportFile = info.bugreportLocationInfo.mBugreportFile;
+ final File dir = bugreportFile.getParentFile();
+ final File tmpZip = new File(dir, "tmp-" + bugreportFile.getName());
Log.d(TAG, "Writing temporary zip file (" + tmpZip + ") with title and/or description");
- try (ZipFile oldZip = new ZipFile(info.bugreportFile);
+ try (ZipFile oldZip = new ZipFile(bugreportFile);
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(tmpZip))) {
// First copy contents from original zip.
@@ -1628,8 +1688,8 @@
stopForegroundWhenDoneLocked(info.id);
}
- if (!tmpZip.renameTo(info.bugreportFile)) {
- Log.e(TAG, "Could not rename " + tmpZip + " to " + info.bugreportFile);
+ if (!tmpZip.renameTo(bugreportFile)) {
+ Log.e(TAG, "Could not rename " + tmpZip + " to " + bugreportFile);
}
}
@@ -2087,15 +2147,9 @@
*/
String formattedLastUpdate;
- /**
- * Path of the main bugreport file.
- */
- File bugreportFile;
+ BugreportLocationInfo bugreportLocationInfo;
- /**
- * Path of the screenshot files.
- */
- List<File> screenshotFiles = new ArrayList<>(1);
+ ScreenshotLocationInfo screenshotLocationInfo;
/**
* Whether dumpstate sent an intent informing it has finished.
@@ -2138,10 +2192,17 @@
/**
* Constructor for tracked bugreports - typically called upon receiving BUGREPORT_REQUESTED.
*/
- BugreportInfo(Context context, String baseName, String name,
- @Nullable String shareTitle, @Nullable String shareDescription,
- @BugreportParams.BugreportMode int type, File bugreportsDir, long nonce,
- @Nullable List<Uri> extraAttachments) {
+ BugreportInfo(
+ Context context,
+ String baseName,
+ String name,
+ @Nullable String shareTitle,
+ @Nullable String shareDescription,
+ @BugreportParams.BugreportMode int type,
+ long nonce,
+ @Nullable List<Uri> extraAttachments,
+ BugreportLocationInfo bugreportLocationInfo,
+ ScreenshotLocationInfo screenshotLocationInfo) {
this.context = context;
this.name = this.initialName = name;
this.shareTitle = shareTitle == null ? "" : shareTitle;
@@ -2149,29 +2210,27 @@
this.type = type;
this.nonce = nonce;
this.baseName = baseName;
- this.bugreportFile = new File(bugreportsDir, getFileName(this, ".zip"));
+ this.bugreportLocationInfo = bugreportLocationInfo;
+ this.screenshotLocationInfo = screenshotLocationInfo;
this.extraAttachments = extraAttachments;
}
- void createBugreportFile() {
- createReadWriteFile(bugreportFile);
- }
-
- void createScreenshotFile(File bugreportsDir) {
+ void maybeCreateScreenshotFile(File bugreportsDir) {
+ if (screenshotLocationInfo.mScreenshotUri != null) {
+ // Screenshot file was already created.
+ return;
+ }
File screenshotFile = new File(bugreportsDir, getScreenshotName("default"));
addScreenshot(screenshotFile);
createReadWriteFile(screenshotFile);
}
ParcelFileDescriptor getBugreportFd() {
- return getFd(bugreportFile);
+ return bugreportLocationInfo.getBugreportFd(context);
}
ParcelFileDescriptor getDefaultScreenshotFd() {
- if (screenshotFiles.isEmpty()) {
- return null;
- }
- return getFd(screenshotFiles.get(0));
+ return screenshotLocationInfo.getScreenshotFd(context);
}
void setTitle(String title) {
@@ -2229,14 +2288,14 @@
* Saves the location of a taken screenshot so it can be sent out at the end.
*/
void addScreenshot(File screenshot) {
- screenshotFiles.add(screenshot);
+ screenshotLocationInfo.mScreenshotFiles.add(screenshot);
}
/**
* Deletes all screenshots taken for a given bugreport.
*/
private void deleteScreenshots() {
- for (File file : screenshotFiles) {
+ for (File file : screenshotLocationInfo.mScreenshotFiles) {
Log.i(TAG, "Deleting screenshot file " + file);
file.delete();
}
@@ -2246,18 +2305,14 @@
* Deletes bugreport file for a given bugreport.
*/
private void deleteBugreportFile() {
- Log.i(TAG, "Deleting bugreport file " + bugreportFile);
- bugreportFile.delete();
+ bugreportLocationInfo.maybeDeleteBugreportFile();
}
/**
* Deletes empty files for a given bugreport.
*/
private void deleteEmptyFiles() {
- if (bugreportFile.length() == 0) {
- Log.i(TAG, "Deleting empty bugreport file: " + bugreportFile);
- bugreportFile.delete();
- }
+ bugreportLocationInfo.maybeDeleteEmptyBugreport();
deleteEmptyScreenshots();
}
@@ -2265,14 +2320,7 @@
* Deletes empty screenshot files.
*/
private void deleteEmptyScreenshots() {
- screenshotFiles.removeIf(file -> {
- final long length = file.length();
- if (length == 0) {
- Log.i(TAG, "Deleting empty screenshot file: " + file);
- file.delete();
- }
- return length == 0;
- });
+ screenshotLocationInfo.deleteEmptyScreenshots();
}
/**
@@ -2280,43 +2328,14 @@
* {@code initialName} if user has changed it.
*/
void renameScreenshots() {
- deleteEmptyScreenshots();
- if (TextUtils.isEmpty(name) || screenshotFiles.isEmpty()) {
- return;
- }
- final List<File> renamedFiles = new ArrayList<>(screenshotFiles.size());
- for (File oldFile : screenshotFiles) {
- final String oldName = oldFile.getName();
- final String newName = oldName.replaceFirst(initialName, name);
- final File newFile;
- if (!newName.equals(oldName)) {
- final File renamedFile = new File(oldFile.getParentFile(), newName);
- Log.d(TAG, "Renaming screenshot file " + oldFile + " to " + renamedFile);
- newFile = oldFile.renameTo(renamedFile) ? renamedFile : oldFile;
- } else {
- Log.w(TAG, "Name didn't change: " + oldName);
- newFile = oldFile;
- }
- if (newFile.length() > 0) {
- renamedFiles.add(newFile);
- } else if (newFile.delete()) {
- Log.d(TAG, "screenshot file: " + newFile + " deleted successfully.");
- }
- }
- screenshotFiles = renamedFiles;
+ screenshotLocationInfo.renameScreenshots(initialName, name);
}
/**
* Rename bugreport file to include the name given by user via UI
*/
void renameBugreportFile() {
- File newBugreportFile = new File(bugreportFile.getParentFile(),
- getFileName(this, ".zip"));
- if (!newBugreportFile.getPath().equals(bugreportFile.getPath())) {
- if (bugreportFile.renameTo(newBugreportFile)) {
- bugreportFile = newBugreportFile;
- }
- }
+ bugreportLocationInfo.maybeRenameBugreportFile(this);
}
String getFormattedLastUpdate() {
@@ -2349,16 +2368,23 @@
builder.append("(").append(description.length()).append(" chars)");
}
- return builder
- .append("\n\tfile: ").append(bugreportFile)
- .append("\n\tscreenshots: ").append(screenshotFiles)
- .append("\n\tprogress: ").append(progress)
- .append("\n\tlast_update: ").append(getFormattedLastUpdate())
- .append("\n\taddingDetailsToZip: ").append(addingDetailsToZip)
- .append(" addedDetailsToZip: ").append(addedDetailsToZip)
- .append("\n\tshareDescription: ").append(shareDescription)
- .append("\n\tshareTitle: ").append(shareTitle)
- .toString();
+ return builder.append("\n\tfile: ")
+ .append(bugreportLocationInfo)
+ .append("\n\tscreenshots: ")
+ .append(screenshotLocationInfo)
+ .append("\n\tprogress: ")
+ .append(progress)
+ .append("\n\tlast_update: ")
+ .append(getFormattedLastUpdate())
+ .append("\n\taddingDetailsToZip: ")
+ .append(addingDetailsToZip)
+ .append(" addedDetailsToZip: ")
+ .append(addedDetailsToZip)
+ .append("\n\tshareDescription: ")
+ .append(shareDescription)
+ .append("\n\tshareTitle: ")
+ .append(shareTitle)
+ .toString();
}
// Parcelable contract
@@ -2375,11 +2401,12 @@
lastProgress.set(in.readInt());
lastUpdate.set(in.readLong());
formattedLastUpdate = in.readString();
- bugreportFile = readFile(in);
+ bugreportLocationInfo = new BugreportLocationInfo(readFile(in));
int screenshotSize = in.readInt();
for (int i = 1; i <= screenshotSize; i++) {
- screenshotFiles.add(readFile(in));
+ screenshotLocationInfo = new ScreenshotLocationInfo(null);
+ screenshotLocationInfo.mScreenshotFiles.add(readFile(in));
}
finished.set(in.readInt() == 1);
@@ -2404,10 +2431,10 @@
dest.writeInt(lastProgress.intValue());
dest.writeLong(lastUpdate.longValue());
dest.writeString(getFormattedLastUpdate());
- writeFile(dest, bugreportFile);
+ writeFile(dest, bugreportLocationInfo.mBugreportFile);
- dest.writeInt(screenshotFiles.size());
- for (File screenshotFile : screenshotFiles) {
+ dest.writeInt(screenshotLocationInfo.mScreenshotFiles.size());
+ for (File screenshotFile : screenshotLocationInfo.mScreenshotFiles) {
writeFile(dest, screenshotFile);
}
@@ -2449,6 +2476,261 @@
};
}
+ /**
+ * Class for abstracting bugreport location. There are two possible cases:
+ * <li>If a bugreport request included a URI for bugreports of type {@link
+ * BugreportParams.BUGREPORT_MODE_WEAR}, then the URI file descriptor will be used. The
+ * requesting app manages the creation and lifecycle of the file.
+ * <li>If no URI is provided in the bugreport request, Shell will create a bugreport file and
+ * manage its lifecycle.
+ */
+ private static final class BugreportLocationInfo {
+ /** Path of the main bugreport file. */
+ @Nullable private File mBugreportFile;
+
+ /** Uri to bugreport location. */
+ @Nullable private Uri mBugreportUri;
+
+ BugreportLocationInfo(File bugreportFile) {
+ this.mBugreportFile = bugreportFile;
+ }
+
+ BugreportLocationInfo(Uri bugreportUri, File bugreportsDir, String baseName, String name) {
+ if (bugreportUri != null) {
+ this.mBugreportUri = bugreportUri;
+ } else {
+ this.mBugreportFile = new File(bugreportsDir, getFileName(".zip", baseName, name));
+ }
+ }
+
+ private boolean maybeCreateBugreportFile() {
+ if (mBugreportFile != null && mBugreportFile.exists()) {
+ Log.e(
+ TAG,
+ "Failed to start bugreport generation, the requested bugreport file "
+ + mBugreportFile
+ + " already exists");
+ return false;
+ }
+ createBugreportFile();
+ return true;
+ }
+
+ private void createBugreportFile() {
+ if (mBugreportUri == null) {
+ createReadWriteFile(mBugreportFile);
+ }
+ }
+
+ private ParcelFileDescriptor getBugreportFd(Context context) {
+ if (mBugreportUri != null) {
+ try {
+ return context.getContentResolver()
+ .openFileDescriptor(mBugreportUri, WRITE_AND_APPEND_MODE);
+ } catch (Exception e) {
+ Log.d(TAG, "Faced exception when getting BR file descriptor", e);
+ return null;
+ }
+ }
+ if (mBugreportFile == null) {
+ Log.e(TAG, "Could not get bugreport file descriptor; bugreport file was null");
+ return null;
+ }
+ return getFd(mBugreportFile);
+ }
+
+ private void maybeDeleteBugreportFile() {
+ if (mBugreportFile == null) {
+ // This means a URI is provided and shell is not responsible for the file's
+ // lifecycle.
+ return;
+ }
+ Log.i(TAG, "Deleting bugreport file " + mBugreportFile);
+ mBugreportFile.delete();
+ }
+
+ private boolean isValidBugreportResult() {
+ if (mBugreportFile != null) {
+ return mBugreportFile.exists() && mBugreportFile.canRead();
+ }
+ // If a bugreport uri was provided, we can't assert on whether the file exists and can
+ // be read. Assume the result is valid.
+ return true;
+ }
+
+ private void maybeDeleteEmptyBugreport() {
+ if (mBugreportFile == null) {
+ // This means a URI is provided and shell is not responsible for the file's
+ // lifecycle.
+ return;
+ }
+ if (mBugreportFile.length() == 0) {
+ Log.i(TAG, "Deleting empty bugreport file: " + mBugreportFile);
+ mBugreportFile.delete();
+ }
+ }
+
+ private void maybeRenameBugreportFile(BugreportInfo bugreportInfo) {
+ if (mBugreportFile == null) {
+ // This means a URI is provided and shell is not responsible for the file's naming.
+ return;
+ }
+ File newBugreportFile =
+ new File(mBugreportFile.getParentFile(), getFileName(bugreportInfo, ".zip"));
+ if (!newBugreportFile.getPath().equals(mBugreportFile.getPath())) {
+ if (mBugreportFile.renameTo(newBugreportFile)) {
+ mBugreportFile = newBugreportFile;
+ }
+ }
+ }
+
+ private boolean isPlainText() {
+ if (mBugreportFile != null) {
+ return mBugreportFile.getName().toLowerCase().endsWith(".txt");
+ }
+ return false;
+ }
+
+ private boolean isFileEmpty(Context context) {
+ if (mBugreportFile != null) {
+ return mBugreportFile.length() == 0;
+ }
+ return getBugreportFd(context).getStatSize() == 0;
+ }
+
+ @Override
+ public String toString() {
+ return "BugreportLocationInfo{"
+ + "bugreportFile="
+ + mBugreportFile
+ + ", bugreportUri="
+ + mBugreportUri
+ + '}';
+ }
+
+ private String getBugreportPath() {
+ if (mBugreportUri != null) {
+ return mBugreportUri.getLastPathSegment();
+ }
+ return mBugreportFile.getAbsolutePath();
+ }
+ }
+
+ /**
+ * Class for abstracting screenshot location. There are two possible cases:
+ * <li>If a bugreport request included a URI for bugreports of type {@link
+ * BugreportParams.BUGREPORT_MODE_WEAR}, then the URI file descriptor will be used. The
+ * requesting app manages the creation and lifecycle of the file.
+ * <li>If no URI is provided in the bugreport request, Shell will create the screenshot file and
+ * manage its lifecycle.
+ */
+ private static final class ScreenshotLocationInfo {
+
+ /** Uri to screenshot location. */
+ @Nullable private Uri mScreenshotUri;
+
+ /** Path to screenshot files. */
+ private List<File> mScreenshotFiles = new ArrayList<>(1);
+
+ ScreenshotLocationInfo(Uri screenshotUri) {
+ if (screenshotUri != null) {
+ this.mScreenshotUri = screenshotUri;
+ }
+ }
+
+ private ParcelFileDescriptor getScreenshotFd(Context context) {
+ if (mScreenshotUri != null) {
+ try {
+ return context.getContentResolver()
+ .openFileDescriptor(mScreenshotUri, WRITE_AND_APPEND_MODE);
+ } catch (Exception e) {
+ Log.d(TAG, "Faced exception when getting screenshot file", e);
+ return null;
+ }
+ }
+
+ if (mScreenshotFiles.isEmpty()) {
+ return null;
+ }
+ return getFd(mScreenshotFiles.getFirst());
+ }
+
+ @Override
+ public String toString() {
+ return "ScreenshotLocationInfo{"
+ + "screenshotUri="
+ + mScreenshotUri
+ + ", screenshotFiles="
+ + mScreenshotFiles
+ + '}';
+ }
+
+ private String getScreenshotPath() {
+ if (mScreenshotUri != null) {
+ return mScreenshotUri.getLastPathSegment();
+ }
+ return getScreenshotForIntent();
+ }
+
+ private void renameScreenshots(String initialName, String name) {
+ if (mScreenshotUri != null) {
+ // If a screenshot uri is provided, then shell is not responsible for the
+ // screenshot's naming.
+ return;
+ }
+ deleteEmptyScreenshots();
+ if (TextUtils.isEmpty(name) || mScreenshotFiles.isEmpty()) {
+ // If there is no user set name for screenshot file or there are no screenshot
+ // files, there's nothing to do.
+ return;
+ }
+ final List<File> renamedFiles = new ArrayList<>(mScreenshotFiles.size());
+ for (File oldFile : mScreenshotFiles) {
+ final String oldName = oldFile.getName();
+ final String newName = oldName.replaceFirst(initialName, name);
+ final File newFile;
+ if (!newName.equals(oldName)) {
+ final File renamedFile = new File(oldFile.getParentFile(), newName);
+ Log.d(TAG, "Renaming screenshot file " + oldFile + " to " + renamedFile);
+ newFile = oldFile.renameTo(renamedFile) ? renamedFile : oldFile;
+ } else {
+ Log.w(TAG, "Name didn't change: " + oldName);
+ newFile = oldFile;
+ }
+ if (newFile.length() > 0) {
+ renamedFiles.add(newFile);
+ } else if (newFile.delete()) {
+ Log.d(TAG, "screenshot file: " + newFile + " deleted successfully.");
+ }
+ }
+ mScreenshotFiles = renamedFiles;
+ }
+
+ private void deleteEmptyScreenshots() {
+ mScreenshotFiles.removeIf(
+ file -> {
+ final long length = file.length();
+ if (length == 0) {
+ Log.i(TAG, "Deleting empty screenshot file: " + file);
+ file.delete();
+ }
+ return length == 0;
+ });
+ }
+
+ /**
+ * Checks if screenshot array is non-empty and returns the first screenshot's path. The
+ * first screenshot is the default screenshot for the bugreport types that take it.
+ */
+ private String getScreenshotForIntent() {
+ if (!mScreenshotFiles.isEmpty()) {
+ final File screenshotFile = mScreenshotFiles.getFirst();
+ return screenshotFile.getAbsolutePath();
+ }
+ return null;
+ }
+ }
+
@GuardedBy("mLock")
private void checkProgressUpdatedLocked(BugreportInfo info, int progress) {
if (progress > CAPPED_PROGRESS) {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e011736..11cb070 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -385,6 +385,9 @@
is ready -->
<uses-permission android:name="android.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW" />
+ <!-- To be able to decipher default applications for certain roles in shortcut helper -->
+ <uses-permission android:name="android.permission.MANAGE_DEFAULT_APPLICATIONS" />
+
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
@@ -459,7 +462,7 @@
android:label="@string/screenshot_scroll_label"
android:finishOnTaskLaunch="true" />
- <service android:name=".screenshot.ScreenshotProxyService"
+ <service android:name=".screenshot.proxy.ScreenshotProxyService"
android:permission="com.android.systemui.permission.SELF"
android:exported="false" />
@@ -490,6 +493,7 @@
<activity android:name=".touchpad.tutorial.ui.view.TouchpadTutorialActivity"
android:exported="true"
android:showForAllUsers="true"
+ android:excludeFromRecents="true"
android:theme="@style/Theme.AppCompat.NoActionBar">
<intent-filter>
<action android:name="com.android.systemui.action.TOUCHPAD_TUTORIAL"/>
@@ -500,6 +504,7 @@
<activity android:name=".inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity"
android:exported="true"
android:showForAllUsers="true"
+ android:excludeFromRecents="true"
android:theme="@style/Theme.AppCompat.NoActionBar">
<intent-filter>
<action android:name="com.android.systemui.action.TOUCHPAD_KEYBOARD_TUTORIAL"/>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-ne/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-ne/strings.xml
index 10e36b8..9506a7e 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-ne/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-ne/strings.xml
@@ -11,7 +11,7 @@
<string name="recent_apps_label" msgid="6583276995616385847">"हालै चलाइएका एप"</string>
<string name="lockscreen_label" msgid="648347953557887087">"लक स्क्रिन"</string>
<string name="quick_settings_label" msgid="2999117381487601865">"द्रुत सेटिङहरू"</string>
- <string name="notifications_label" msgid="6829741046963013567">"सूचनाहरू"</string>
+ <string name="notifications_label" msgid="6829741046963013567">"नोटिफिकेसनहरू"</string>
<string name="screenshot_label" msgid="863978141223970162">"स्क्रिनसट"</string>
<string name="screenshot_utterance" msgid="1430760563401895074">"स्क्रिनसट लिनुहोस्"</string>
<string name="volume_up_label" msgid="8592766918780362870">"भोल्युम बढाउनुहोस्"</string>
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 90434f5..ee22915 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1230,13 +1230,6 @@
}
flag {
- name: "communal_hub_on_mobile"
- namespace: "systemui"
- description: "Brings the glanceable hub experience to mobile phones"
- bug: "375689917"
-}
-
-flag {
name: "glanceable_hub_v2"
namespace: "systemui"
description: "Gates the refreshed glanceable hub experience that also brings the glanceable hub to mobile phones"
@@ -1801,16 +1794,6 @@
}
flag {
- name: "ensure_enr_views_visibility"
- namespace: "systemui"
- description: "Ensures public and private visibilities"
- bug: "361552380"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "shade_expands_on_status_bar_long_press"
namespace: "systemui"
description: "Expands the shade on long press of any status bar"
@@ -1889,3 +1872,10 @@
description: "Implement the depth push scaling effect on the current app when users pull down shade."
bug: "370560660"
}
+
+flag {
+ name: "expanded_privacy_indicators_on_large_screen"
+ namespace: "systemui"
+ description: "Larger privacy indicators on large screen"
+ bug: "381864715"
+}
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
index cec740a..0317d5f0 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
@@ -57,6 +57,14 @@
mView = view;
}
+ /**
+ * @return the view wrapped by this UI component.
+ * @hide
+ */
+ public View getView() {
+ return mView;
+ }
+
@Override
public float getAlpha() {
return mView.getAlpha();
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index 3eeaf41..41a00f5 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -873,6 +873,9 @@
) {
// Raise closing task to "above" layer so it isn't covered.
t.setLayer(target.leash, aboveLayers - i)
+ } else if (TransitionUtil.isOpeningType(change.mode)) {
+ // Put into the "below" layer space.
+ t.setLayer(target.leash, belowLayers - i)
}
} else if (TransitionInfo.isIndependent(change, info)) {
// Root tasks
@@ -1153,7 +1156,7 @@
// If a [controller.windowAnimatorState] exists, treat this like a takeover.
takeOverAnimationInternal(
window,
- startWindowStates = null,
+ startWindowState = null,
startTransaction = null,
callback,
)
@@ -1168,22 +1171,23 @@
callback: IRemoteAnimationFinishedCallback?,
) {
val window = setUpAnimation(apps, callback) ?: return
- takeOverAnimationInternal(window, startWindowStates, startTransaction, callback)
+ val startWindowState = startWindowStates[apps!!.indexOf(window)]
+ takeOverAnimationInternal(window, startWindowState, startTransaction, callback)
}
private fun takeOverAnimationInternal(
window: RemoteAnimationTarget,
- startWindowStates: Array<WindowAnimationState>?,
+ startWindowState: WindowAnimationState?,
startTransaction: SurfaceControl.Transaction?,
callback: IRemoteAnimationFinishedCallback?,
) {
val useSpring =
- !controller.isLaunching && startWindowStates != null && startTransaction != null
+ !controller.isLaunching && startWindowState != null && startTransaction != null
startAnimation(
window,
navigationBar = null,
useSpring,
- startWindowStates,
+ startWindowState,
startTransaction,
callback,
)
@@ -1293,7 +1297,7 @@
window: RemoteAnimationTarget,
navigationBar: RemoteAnimationTarget? = null,
useSpring: Boolean = false,
- startingWindowStates: Array<WindowAnimationState>? = null,
+ startingWindowState: WindowAnimationState? = null,
startTransaction: SurfaceControl.Transaction? = null,
iCallback: IRemoteAnimationFinishedCallback? = null,
) {
@@ -1339,6 +1343,7 @@
val isExpandingFullyAbove =
transitionAnimator.isExpandingFullyAbove(controller.transitionContainer, endState)
+ val windowState = startingWindowState ?: controller.windowAnimatorState
// We animate the opening window and delegate the view expansion to [this.controller].
val delegate = this.controller
@@ -1361,18 +1366,6 @@
}
}
- // The states are sorted matching the changes inside the transition info.
- // Using this info, the RemoteAnimationTargets are created, with their
- // prefixOrderIndex fields in reverse order to that of changes. To extract
- // the right state, we need to invert again.
- val windowState =
- if (startingWindowStates != null) {
- startingWindowStates[
- startingWindowStates.size - window.prefixOrderIndex]
- } else {
- controller.windowAnimatorState
- }
-
// TODO(b/323863002): use the timestamp and velocity to update the initial
// position.
val bounds = windowState?.bounds
@@ -1461,12 +1454,6 @@
delegate.onTransitionAnimationProgress(state, progress, linearProgress)
}
}
- val windowState =
- if (startingWindowStates != null) {
- startingWindowStates[startingWindowStates.size - window.prefixOrderIndex]
- } else {
- controller.windowAnimatorState
- }
val velocityPxPerS =
if (longLivedReturnAnimationsEnabled() && windowState?.velocityPxPerMs != null) {
val xVelocityPxPerS = windowState.velocityPxPerMs.x * 1000
@@ -1485,6 +1472,7 @@
fadeWindowBackgroundLayer = !controller.isBelowAnimatingWindow,
drawHole = !controller.isBelowAnimatingWindow,
startVelocity = velocityPxPerS,
+ startFrameTime = windowState?.timestamp ?: -1,
)
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
index 558c1eba..65cd3c7 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
@@ -397,6 +397,10 @@
ghostedView.visibility = View.VISIBLE
ghostedView.invalidate()
}
+
+ if (isEphemeral) {
+ onDispose()
+ }
}
companion object {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
index e2bc409..4e889e9 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
@@ -27,6 +27,8 @@
import android.util.FloatProperty
import android.util.Log
import android.util.MathUtils
+import android.util.TimeUtils
+import android.view.Choreographer
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroupOverlay
@@ -366,6 +368,7 @@
@get:VisibleForTesting val springY: SpringAnimation,
@get:VisibleForTesting val springScale: SpringAnimation,
private val springState: SpringState,
+ private val startFrameTime: Long,
private val onAnimationStart: Runnable,
) : Animation {
@get:VisibleForTesting
@@ -374,6 +377,42 @@
override fun start() {
onAnimationStart.run()
+
+ // If no start frame time is provided, we start the springs normally.
+ if (startFrameTime < 0) {
+ startSprings()
+ return
+ }
+
+ // This function is not guaranteed to be called inside a frame. We try to access the
+ // frame time immediately, but if we're not inside a frame this will throw an exception.
+ // We must then post a callback to be run at the beginning of the next frame.
+ try {
+ initAndStartSprings(Choreographer.getInstance().frameTime)
+ } catch (_: IllegalStateException) {
+ Choreographer.getInstance().postFrameCallback { frameTimeNanos ->
+ initAndStartSprings(frameTimeNanos / TimeUtils.NANOS_PER_MS)
+ }
+ }
+ }
+
+ private fun initAndStartSprings(frameTime: Long) {
+ // Initialize the spring as if it had started at the time that its start state
+ // was created.
+ springX.doAnimationFrame(startFrameTime)
+ springY.doAnimationFrame(startFrameTime)
+ springScale.doAnimationFrame(startFrameTime)
+ // Move the spring time forward to the current frame, so it updates its internal state
+ // following the initial momentum over the elapsed time.
+ springX.doAnimationFrame(frameTime)
+ springY.doAnimationFrame(frameTime)
+ springScale.doAnimationFrame(frameTime)
+ // Actually start the spring. We do this after the previous calls because the framework
+ // doesn't like it when you call doAnimationFrame() after start() with an earlier time.
+ startSprings()
+ }
+
+ private fun startSprings() {
springX.start()
springY.start()
springScale.start()
@@ -471,7 +510,9 @@
* is true.
*
* If [startVelocity] (expressed in pixels per second) is not null, a multi-spring animation
- * using it for the initial momentum will be used instead of the default interpolators.
+ * using it for the initial momentum will be used instead of the default interpolators. In this
+ * case, [startFrameTime] (if non-negative) represents the frame time at which the springs
+ * should be started.
*/
fun startAnimation(
controller: Controller,
@@ -480,6 +521,7 @@
fadeWindowBackgroundLayer: Boolean = true,
drawHole: Boolean = false,
startVelocity: PointF? = null,
+ startFrameTime: Long = -1,
): Animation {
if (!controller.isLaunching) assertReturnAnimations()
if (startVelocity != null) assertLongLivedReturnAnimations()
@@ -502,6 +544,7 @@
fadeWindowBackgroundLayer,
drawHole,
startVelocity,
+ startFrameTime,
)
.apply { start() }
}
@@ -515,6 +558,7 @@
fadeWindowBackgroundLayer: Boolean = true,
drawHole: Boolean = false,
startVelocity: PointF? = null,
+ startFrameTime: Long = -1,
): Animation {
val transitionContainer = controller.transitionContainer
val transitionContainerOverlay = transitionContainer.overlay
@@ -537,6 +581,7 @@
startState,
endState,
startVelocity,
+ startFrameTime,
windowBackgroundLayer,
transitionContainer,
transitionContainerOverlay,
@@ -722,6 +767,7 @@
startState: State,
endState: State,
startVelocity: PointF,
+ startFrameTime: Long,
windowBackgroundLayer: GradientDrawable,
transitionContainer: View,
transitionContainerOverlay: ViewGroupOverlay,
@@ -912,7 +958,7 @@
}
}
- return MultiSpringAnimation(springX, springY, springScale, springState) {
+ return MultiSpringAnimation(springX, springY, springScale, springState, startFrameTime) {
onAnimationStart(
controller,
isExpandingFullyAbove,
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/ShadeDisplayAwareDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/ShadeDisplayAwareDetector.kt
index 2f83d82..92b6fd4 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/ShadeDisplayAwareDetector.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/ShadeDisplayAwareDetector.kt
@@ -25,6 +25,7 @@
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiParameter
import org.jetbrains.uast.UClass
import org.jetbrains.uast.getContainingUFile
@@ -40,14 +41,8 @@
if (!isInRelevantShadePackage(node)) continue
if (IGNORED_PACKAGES.contains(node.qualifiedName)) continue
- // Check the any context-dependent parameter to see if it has @ShadeDisplayAware
- // annotation
for (parameter in constructor.parameterList.parameters) {
- val shouldReport =
- CONTEXT_DEPENDENT_SHADE_CLASSES.contains(
- parameter.type.canonicalText
- ) && !parameter.hasAnnotation(SHADE_DISPLAY_AWARE_ANNOTATION)
- if (shouldReport) {
+ if (parameter.shouldReport()) {
context.report(
issue = ISSUE,
scope = parameter.declarationScope,
@@ -62,20 +57,35 @@
companion object {
private const val INJECT_ANNOTATION = "javax.inject.Inject"
+ private const val APPLICATION_ANNOTATION =
+ "com.android.systemui.dagger.qualifiers.Application"
+ private const val GLOBAL_CONFIG_ANNOTATION = "com.android.systemui.common.ui.GlobalConfig"
private const val SHADE_DISPLAY_AWARE_ANNOTATION =
"com.android.systemui.shade.ShadeDisplayAware"
+ private const val CONTEXT = "android.content.Context"
+ private const val WINDOW_MANAGER = "android.view.WindowManager"
+ private const val LAYOUT_INFLATER = "android.view.LayoutInflater"
+ private const val RESOURCES = "android.content.res.Resources"
+ private const val CONFIG_STATE = "com.android.systemui.common.ui.ConfigurationState"
+ private const val CONFIG_CONTROLLER =
+ "com.android.systemui.statusbar.policy.ConfigurationController"
+ private const val CONFIG_INTERACTOR =
+ "com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor"
+
private val CONTEXT_DEPENDENT_SHADE_CLASSES =
setOf(
- "android.content.Context",
- "android.view.WindowManager",
- "android.view.LayoutInflater",
- "android.content.res.Resources",
- "com.android.systemui.common.ui.ConfigurationState",
- "com.android.systemui.statusbar.policy.ConfigurationController",
- "com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor",
+ CONTEXT,
+ WINDOW_MANAGER,
+ LAYOUT_INFLATER,
+ RESOURCES,
+ CONFIG_STATE,
+ CONFIG_CONTROLLER,
+ CONFIG_INTERACTOR,
)
+ private val CONFIG_CLASSES = setOf(CONFIG_STATE, CONFIG_CONTROLLER, CONFIG_INTERACTOR)
+
private val SHADE_WINDOW_PACKAGES =
listOf(
"com.android.systemui.biometrics",
@@ -93,6 +103,22 @@
"com.android.systemui.qs.customize.TileAdapter",
)
+ private fun PsiParameter.shouldReport(): Boolean {
+ val className = type.canonicalText
+
+ // check if the parameter is a context-dependent class relevant to shade
+ if (className !in CONTEXT_DEPENDENT_SHADE_CLASSES) return false
+ // check if it has @ShadeDisplayAware
+ if (hasAnnotation(SHADE_DISPLAY_AWARE_ANNOTATION)) return false
+ // check if its a @Application-annotated Context
+ if (className == CONTEXT && hasAnnotation(APPLICATION_ANNOTATION)) return false
+ // check if its a @GlobalConfig-annotated ConfigurationState, ConfigurationController
+ // or ConfigurationInteractor
+ if (className in CONFIG_CLASSES && hasAnnotation(GLOBAL_CONFIG_ANNOTATION)) return false
+
+ return true
+ }
+
private fun isInRelevantShadePackage(node: UClass): Boolean {
val packageName = node.getContainingUFile()?.packageName
if (packageName.isNullOrBlank()) return false
@@ -102,15 +128,15 @@
}
private fun reportMsg(className: String) =
- """UI elements of the shade window
- |should use ShadeDisplayAware-annotated $className, as the shade might move between windows, and only
- |@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so
- |might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).
- |If the usage of $className is not related to display specific configuration or UI, then there is
- |technically no need to use the annotation, and you can annotate the class with
- |@SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")
- |"""
- .trimMargin()
+ "UI elements of the shade window should use " +
+ "ShadeDisplayAware-annotated $className, as the shade might move between windows, " +
+ "and only @ShadeDisplayAware resources are updated with the new configuration " +
+ "correctly. Failures to do so might result in wrong dimensions for shade window " +
+ "classes (e.g. using the wrong density or theme). If the usage of $className is " +
+ "not related to display specific configuration or UI, then there is technically " +
+ "no need to use the annotation, and you can annotate the class with " +
+ "@SuppressLint(\"ShadeDisplayAwareContextChecker\")/" +
+ "@Suppress(\"ShadeDisplayAwareContextChecker\")".trimMargin()
@JvmField
val ISSUE: Issue =
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/ShadeDisplayAwareDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/ShadeDisplayAwareDetectorTest.kt
index 58ad363..79f1907 100644
--- a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/ShadeDisplayAwareDetectorTest.kt
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/ShadeDisplayAwareDetectorTest.kt
@@ -63,6 +63,26 @@
)
.indented()
+ private val applicationStub: TestFile =
+ kotlin(
+ """
+ package com.android.systemui.dagger.qualifiers
+
+ @Retention(AnnotationRetention.RUNTIME) annotation class Application
+ """
+ )
+ .indented()
+
+ private val globalConfigStub: TestFile =
+ kotlin(
+ """
+ package com.android.systemui.common.ui
+
+ @Retention(AnnotationRetention.RUNTIME) annotation class GlobalConfig
+ """
+ )
+ .indented()
+
private val configStateStub: TestFile =
kotlin(
"""
@@ -98,6 +118,8 @@
injectStub,
qsContext,
shadeDisplayAwareStub,
+ applicationStub,
+ globalConfigStub,
configStateStub,
configControllerStub,
configInteractorStub,
@@ -259,6 +281,60 @@
}
@Test
+ fun injectedConstructor_inRelevantPackage_withApplicationAnnotatedContext() {
+ lint()
+ .files(
+ TestFiles.kotlin(
+ """
+ package com.android.systemui.shade.example
+
+ import javax.inject.Inject
+ import android.content.Context
+ import com.android.systemui.dagger.qualifiers.Application
+
+ class ExampleClass
+ @Inject
+ constructor(@Application private val context: Context)
+ """
+ .trimIndent()
+ ),
+ *androidStubs,
+ *otherStubs,
+ )
+ .issues(ShadeDisplayAwareDetector.ISSUE)
+ .testModes(TestMode.DEFAULT)
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun injectedConstructor_inRelevantPackage_withGlobalConfigAnnotatedConfigurationClass() {
+ lint()
+ .files(
+ TestFiles.kotlin(
+ """
+ package com.android.systemui.shade.example
+
+ import javax.inject.Inject
+ import com.android.systemui.common.ui.ConfigurationState
+ import com.android.systemui.common.ui.GlobalConfig
+
+ class ExampleClass
+ @Inject
+ constructor(@GlobalConfig private val configState: ConfigurationState)
+ """
+ .trimIndent()
+ ),
+ *androidStubs,
+ *otherStubs,
+ )
+ .issues(ShadeDisplayAwareDetector.ISSUE)
+ .testModes(TestMode.DEFAULT)
+ .run()
+ .expectClean()
+ }
+
+ @Test
fun injectedConstructor_notInRelevantPackage_withRelevantParameter_withoutAnnotation() {
lint()
.files(
@@ -363,13 +439,13 @@
}
private fun errorMsgString(lineNumber: Int, className: String) =
- """
- src/com/android/systemui/shade/example/ExampleClass.kt:$lineNumber: Error: UI elements of the shade window
- should use ShadeDisplayAware-annotated $className, as the shade might move between windows, and only
- @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so
- might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).
- If the usage of $className is not related to display specific configuration or UI, then there is
- technically no need to use the annotation, and you can annotate the class with
- @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")
- """
+ "src/com/android/systemui/shade/example/ExampleClass.kt:$lineNumber: Error: UI elements of " +
+ "the shade window should use ShadeDisplayAware-annotated $className, as the shade " +
+ "might move between windows, and only @ShadeDisplayAware resources are updated with " +
+ "the new configuration correctly. Failures to do so might result in wrong dimensions " +
+ "for shade window classes (e.g. using the wrong density or theme). If the usage of " +
+ "$className is not related to display specific configuration or UI, then there is " +
+ "technically no need to use the annotation, and you can annotate the class with " +
+ "@SuppressLint(\"ShadeDisplayAwareContextChecker\")" +
+ "/@Suppress(\"ShadeDisplayAwareContextChecker\")"
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
new file mode 100644
index 0000000..9fe85b7
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.gesture
+
+import androidx.compose.foundation.OverscrollEffect
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.gestures.awaitHorizontalTouchSlopOrCancellation
+import androidx.compose.foundation.gestures.awaitVerticalTouchSlopOrCancellation
+import androidx.compose.foundation.gestures.horizontalDrag
+import androidx.compose.foundation.gestures.verticalDrag
+import androidx.compose.foundation.overscroll
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.nestedScrollModifierNode
+import androidx.compose.ui.input.pointer.AwaitPointerEventScope
+import androidx.compose.ui.input.pointer.PointerEvent
+import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.PointerId
+import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.PointerInputScope
+import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
+import androidx.compose.ui.input.pointer.positionChange
+import androidx.compose.ui.input.pointer.util.VelocityTracker
+import androidx.compose.ui.input.pointer.util.addPointerInputChange
+import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
+import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.PointerInputModifierNode
+import androidx.compose.ui.node.currentValueOf
+import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.Velocity
+import androidx.compose.ui.util.fastAny
+import com.android.compose.modifiers.thenIf
+import kotlin.math.sign
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.launch
+
+/**
+ * A draggable that plays nicely with the nested scroll mechanism.
+ *
+ * This can be used whenever you need a draggable inside a scrollable or a draggable that contains a
+ * scrollable.
+ */
+interface NestedDraggable {
+ /**
+ * Called when a drag is started in the given [position] (*before* dragging the touch slop) and
+ * in the direction given by [sign].
+ */
+ fun onDragStarted(position: Offset, sign: Float): Controller
+
+ /**
+ * Whether this draggable should consume any scroll amount with the given [sign] coming from a
+ * nested scrollable.
+ *
+ * This is called whenever a nested scrollable does not consume some scroll amount. If this
+ * returns `true`, then [onDragStarted] will be called and this draggable will have priority and
+ * consume all future events during preScroll until the nested scroll is finished.
+ */
+ fun shouldConsumeNestedScroll(sign: Float): Boolean
+
+ interface Controller {
+ /**
+ * Drag by [delta] pixels.
+ *
+ * @return the consumed [delta]. Any non-consumed delta will be dispatched to the next
+ * nested scroll connection to be consumed by any composable above in the hierarchy. If
+ * the drag was performed on this draggable directly (instead of on a nested scrollable),
+ * any remaining delta will be used to overscroll this draggable.
+ */
+ fun onDrag(delta: Float): Float
+
+ /**
+ * Stop the current drag with the given [velocity].
+ *
+ * @return the consumed [velocity]. Any non-consumed velocity will be dispatched to the next
+ * nested scroll connection to be consumed by any composable above in the hierarchy. If
+ * the drag was performed on this draggable directly (instead of on a nested scrollable),
+ * any remaining velocity will be used to animate the overscroll of this draggable.
+ */
+ suspend fun onDragStopped(velocity: Float): Float
+ }
+}
+
+/**
+ * A draggable that supports nested scrolling and overscroll effects.
+ *
+ * @see NestedDraggable
+ */
+fun Modifier.nestedDraggable(
+ draggable: NestedDraggable,
+ orientation: Orientation,
+ overscrollEffect: OverscrollEffect? = null,
+ enabled: Boolean = true,
+): Modifier {
+ return this.thenIf(overscrollEffect != null) { Modifier.overscroll(overscrollEffect) }
+ .then(NestedDraggableElement(draggable, orientation, overscrollEffect, enabled))
+}
+
+private data class NestedDraggableElement(
+ private val draggable: NestedDraggable,
+ private val orientation: Orientation,
+ private val overscrollEffect: OverscrollEffect?,
+ private val enabled: Boolean,
+) : ModifierNodeElement<NestedDraggableNode>() {
+ override fun create(): NestedDraggableNode {
+ return NestedDraggableNode(draggable, orientation, overscrollEffect, enabled)
+ }
+
+ override fun update(node: NestedDraggableNode) {
+ node.update(draggable, orientation, overscrollEffect, enabled)
+ }
+}
+
+private class NestedDraggableNode(
+ private var draggable: NestedDraggable,
+ override var orientation: Orientation,
+ private var overscrollEffect: OverscrollEffect?,
+ private var enabled: Boolean,
+) :
+ DelegatingNode(),
+ PointerInputModifierNode,
+ NestedScrollConnection,
+ CompositionLocalConsumerModifierNode,
+ OrientationAware {
+ private val nestedScrollDispatcher = NestedScrollDispatcher()
+ private var trackDownPositionDelegate: SuspendingPointerInputModifierNode? = null
+ set(value) {
+ field?.let { undelegate(it) }
+ field = value?.also { delegate(it) }
+ }
+
+ private var detectDragsDelegate: SuspendingPointerInputModifierNode? = null
+ set(value) {
+ field?.let { undelegate(it) }
+ field = value?.also { delegate(it) }
+ }
+
+ /** The controller created by the nested scroll logic (and *not* the drag logic). */
+ private var nestedScrollController: WrappedController? = null
+ set(value) {
+ field?.ensureOnDragStoppedIsCalled()
+ field = value
+ }
+
+ /**
+ * The last pointer which was the first down since the last time all pointers were up.
+ *
+ * This is use to track the started position of a drag started on a nested scrollable.
+ */
+ private var lastFirstDown: Offset? = null
+
+ init {
+ delegate(nestedScrollModifierNode(this, nestedScrollDispatcher))
+ }
+
+ override fun onDetach() {
+ nestedScrollController?.ensureOnDragStoppedIsCalled()
+ }
+
+ fun update(
+ draggable: NestedDraggable,
+ orientation: Orientation,
+ overscrollEffect: OverscrollEffect?,
+ enabled: Boolean,
+ ) {
+ this.draggable = draggable
+ this.orientation = orientation
+ this.overscrollEffect = overscrollEffect
+ this.enabled = enabled
+
+ trackDownPositionDelegate?.resetPointerInputHandler()
+ detectDragsDelegate?.resetPointerInputHandler()
+ nestedScrollController?.ensureOnDragStoppedIsCalled()
+
+ if (!enabled && trackDownPositionDelegate != null) {
+ check(detectDragsDelegate != null)
+ trackDownPositionDelegate = null
+ detectDragsDelegate = null
+ }
+ }
+
+ override fun onPointerEvent(
+ pointerEvent: PointerEvent,
+ pass: PointerEventPass,
+ bounds: IntSize,
+ ) {
+ if (!enabled) return
+
+ if (trackDownPositionDelegate == null) {
+ check(detectDragsDelegate == null)
+ trackDownPositionDelegate = SuspendingPointerInputModifierNode { trackDownPosition() }
+ detectDragsDelegate = SuspendingPointerInputModifierNode { detectDrags() }
+ }
+
+ checkNotNull(trackDownPositionDelegate).onPointerEvent(pointerEvent, pass, bounds)
+ checkNotNull(detectDragsDelegate).onPointerEvent(pointerEvent, pass, bounds)
+ }
+
+ override fun onCancelPointerInput() {
+ trackDownPositionDelegate?.onCancelPointerInput()
+ detectDragsDelegate?.onCancelPointerInput()
+ }
+
+ /*
+ * ======================================
+ * ===== Pointer input (drag) logic =====
+ * ======================================
+ */
+
+ private suspend fun PointerInputScope.detectDrags() {
+ // Lazily create the velocity tracker when the pointer input restarts.
+ val velocityTracker = VelocityTracker()
+
+ awaitEachGesture {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ var overSlop = 0f
+ val onTouchSlopReached = { change: PointerInputChange, over: Float ->
+ change.consume()
+ overSlop = over
+ }
+
+ suspend fun AwaitPointerEventScope.awaitTouchSlopOrCancellation(
+ pointerId: PointerId
+ ): PointerInputChange? {
+ return when (orientation) {
+ Orientation.Horizontal ->
+ awaitHorizontalTouchSlopOrCancellation(pointerId, onTouchSlopReached)
+ Orientation.Vertical ->
+ awaitVerticalTouchSlopOrCancellation(pointerId, onTouchSlopReached)
+ }
+ }
+
+ var drag = awaitTouchSlopOrCancellation(down.id)
+
+ // We try to pick-up the drag gesture in case the touch slop swipe was consumed by a
+ // nested scrollable child that disappeared.
+ // This was copied from http://shortn/_10L8U02IoL.
+ // TODO(b/380838584): Reuse detect(Horizontal|Vertical)DragGestures() instead.
+ while (drag == null && currentEvent.changes.fastAny { it.pressed }) {
+ var event: PointerEvent
+ do {
+ event = awaitPointerEvent()
+ } while (
+ event.changes.fastAny { it.isConsumed } && event.changes.fastAny { it.pressed }
+ )
+
+ // An event was not consumed and there's still a pointer in the screen.
+ if (event.changes.fastAny { it.pressed }) {
+ // Await touch slop again, using the initial down as starting point.
+ // For most cases this should return immediately since we probably moved
+ // far enough from the initial down event.
+ drag = awaitTouchSlopOrCancellation(down.id)
+ }
+ }
+
+ if (drag != null) {
+ velocityTracker.resetTracking()
+
+ val sign = (drag.position - down.position).toFloat().sign
+ val wrappedController =
+ WrappedController(coroutineScope, draggable.onDragStarted(down.position, sign))
+ if (overSlop != 0f) {
+ onDrag(wrappedController, drag, overSlop, velocityTracker)
+ }
+
+ // If a drag was started, we cancel any other drag started by a nested scrollable.
+ //
+ // Note: we cancel the nested drag here *after* starting the new drag so that in the
+ // STL case, the cancelled drag will not change the current scene of the STL.
+ nestedScrollController?.ensureOnDragStoppedIsCalled()
+
+ val isSuccessful =
+ try {
+ val onDrag = { change: PointerInputChange ->
+ onDrag(
+ wrappedController,
+ change,
+ change.positionChange().toFloat(),
+ velocityTracker,
+ )
+ change.consume()
+ }
+
+ when (orientation) {
+ Orientation.Horizontal -> horizontalDrag(drag.id, onDrag)
+ Orientation.Vertical -> verticalDrag(drag.id, onDrag)
+ }
+ } catch (t: Throwable) {
+ wrappedController.ensureOnDragStoppedIsCalled()
+ throw t
+ }
+
+ if (isSuccessful) {
+ val maxVelocity = currentValueOf(LocalViewConfiguration).maximumFlingVelocity
+ val velocity =
+ velocityTracker
+ .calculateVelocity(Velocity(maxVelocity, maxVelocity))
+ .toFloat()
+ onDragStopped(wrappedController, velocity)
+ } else {
+ onDragStopped(wrappedController, velocity = 0f)
+ }
+ }
+ }
+ }
+
+ private fun onDrag(
+ controller: NestedDraggable.Controller,
+ change: PointerInputChange,
+ delta: Float,
+ velocityTracker: VelocityTracker,
+ ) {
+ velocityTracker.addPointerInputChange(change)
+
+ scrollWithOverscroll(delta) { deltaFromOverscroll ->
+ scrollWithNestedScroll(deltaFromOverscroll) { deltaFromNestedScroll ->
+ controller.onDrag(deltaFromNestedScroll)
+ }
+ }
+ }
+
+ private fun onDragStopped(controller: WrappedController, velocity: Float) {
+ coroutineScope.launch(start = CoroutineStart.UNDISPATCHED) {
+ try {
+ flingWithOverscroll(velocity) { velocityFromOverscroll ->
+ flingWithNestedScroll(velocityFromOverscroll) { velocityFromNestedScroll ->
+ controller.onDragStopped(velocityFromNestedScroll)
+ }
+ }
+ } finally {
+ controller.ensureOnDragStoppedIsCalled()
+ }
+ }
+ }
+
+ private fun scrollWithOverscroll(delta: Float, performScroll: (Float) -> Float): Float {
+ val effect = overscrollEffect
+ return if (effect != null) {
+ effect
+ .applyToScroll(delta.toOffset(), source = NestedScrollSource.UserInput) {
+ performScroll(it.toFloat()).toOffset()
+ }
+ .toFloat()
+ } else {
+ performScroll(delta)
+ }
+ }
+
+ private fun scrollWithNestedScroll(delta: Float, performScroll: (Float) -> Float): Float {
+ val preConsumed =
+ nestedScrollDispatcher
+ .dispatchPreScroll(
+ available = delta.toOffset(),
+ source = NestedScrollSource.UserInput,
+ )
+ .toFloat()
+ val available = delta - preConsumed
+ val consumed = performScroll(available)
+ val left = available - consumed
+ val postConsumed =
+ nestedScrollDispatcher
+ .dispatchPostScroll(
+ consumed = (preConsumed + consumed).toOffset(),
+ available = left.toOffset(),
+ source = NestedScrollSource.UserInput,
+ )
+ .toFloat()
+ return consumed + preConsumed + postConsumed
+ }
+
+ private suspend fun flingWithOverscroll(
+ velocity: Float,
+ performFling: suspend (Float) -> Float,
+ ) {
+ val effect = overscrollEffect
+ if (effect != null) {
+ effect.applyToFling(velocity.toVelocity()) { performFling(it.toFloat()).toVelocity() }
+ } else {
+ performFling(velocity)
+ }
+ }
+
+ private suspend fun flingWithNestedScroll(
+ velocity: Float,
+ performFling: suspend (Float) -> Float,
+ ): Float {
+ val preConsumed = nestedScrollDispatcher.dispatchPreFling(available = velocity.toVelocity())
+ val available = velocity - preConsumed.toFloat()
+ val consumed = performFling(available)
+ val left = available - consumed
+ return nestedScrollDispatcher
+ .dispatchPostFling(
+ consumed = consumed.toVelocity() + preConsumed,
+ available = left.toVelocity(),
+ )
+ .toFloat()
+ }
+
+ /*
+ * ===============================
+ * ===== Nested scroll logic =====
+ * ===============================
+ */
+
+ private suspend fun PointerInputScope.trackDownPosition() {
+ awaitEachGesture { lastFirstDown = awaitFirstDown(requireUnconsumed = false).position }
+ }
+
+ override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+ val controller = nestedScrollController ?: return Offset.Zero
+ val consumed = controller.onDrag(available.toFloat())
+ return consumed.toOffset()
+ }
+
+ override fun onPostScroll(
+ consumed: Offset,
+ available: Offset,
+ source: NestedScrollSource,
+ ): Offset {
+ if (source == NestedScrollSource.SideEffect) {
+ check(nestedScrollController == null)
+ return Offset.Zero
+ }
+
+ val offset = available.toFloat()
+ if (offset == 0f) {
+ return Offset.Zero
+ }
+
+ val sign = offset.sign
+ if (nestedScrollController == null && draggable.shouldConsumeNestedScroll(sign)) {
+ val startedPosition = checkNotNull(lastFirstDown) { "lastFirstDown is not set" }
+ nestedScrollController =
+ WrappedController(coroutineScope, draggable.onDragStarted(startedPosition, sign))
+ }
+
+ val controller = nestedScrollController ?: return Offset.Zero
+ return controller.onDrag(offset).toOffset()
+ }
+
+ override suspend fun onPreFling(available: Velocity): Velocity {
+ val controller = nestedScrollController ?: return Velocity.Zero
+ nestedScrollController = null
+
+ val consumed = controller.onDragStopped(available.toFloat())
+ return consumed.toVelocity()
+ }
+}
+
+/**
+ * A controller that wraps [delegate] and can be used to ensure that [onDragStopped] is called, but
+ * not more than once.
+ */
+private class WrappedController(
+ private val coroutineScope: CoroutineScope,
+ private val delegate: NestedDraggable.Controller,
+) : NestedDraggable.Controller by delegate {
+ private var onDragStoppedCalled = false
+
+ override fun onDrag(delta: Float): Float {
+ if (onDragStoppedCalled) return 0f
+ return delegate.onDrag(delta)
+ }
+
+ override suspend fun onDragStopped(velocity: Float): Float {
+ if (onDragStoppedCalled) return 0f
+ onDragStoppedCalled = true
+ return delegate.onDragStopped(velocity)
+ }
+
+ fun ensureOnDragStoppedIsCalled() {
+ // Start with UNDISPATCHED so that onDragStopped() is always run until its first suspension
+ // point, even if coroutineScope is cancelled.
+ coroutineScope.launch(start = CoroutineStart.UNDISPATCHED) { onDragStopped(velocity = 0f) }
+ }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/OrientationAware.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/OrientationAware.kt
new file mode 100644
index 0000000..6e91727
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/OrientationAware.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.compose.gesture
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.unit.Velocity
+
+/**
+ * An interface to conveniently convert a [Float] to and from an [Offset] or a [Velocity] given an
+ * [orientation].
+ */
+interface OrientationAware {
+ val orientation: Orientation
+
+ fun Float.toOffset(): Offset {
+ return when (orientation) {
+ Orientation.Horizontal -> Offset(x = this, y = 0f)
+ Orientation.Vertical -> Offset(x = 0f, y = this)
+ }
+ }
+
+ fun Float.toVelocity(): Velocity {
+ return when (orientation) {
+ Orientation.Horizontal -> Velocity(x = this, y = 0f)
+ Orientation.Vertical -> Velocity(x = 0f, y = this)
+ }
+ }
+
+ fun Offset.toFloat(): Float {
+ return when (orientation) {
+ Orientation.Horizontal -> this.x
+ Orientation.Vertical -> this.y
+ }
+ }
+
+ fun Velocity.toFloat(): Float {
+ return when (orientation) {
+ Orientation.Horizontal -> this.x
+ Orientation.Vertical -> this.y
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
new file mode 100644
index 0000000..fd3902f
--- /dev/null
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.gesture
+
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.test.swipeDown
+import androidx.compose.ui.test.swipeLeft
+import androidx.compose.ui.unit.Velocity
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.awaitCancellation
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class NestedDraggableTest(override val orientation: Orientation) : OrientationAware {
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun orientations() = listOf(Orientation.Horizontal, Orientation.Vertical)
+ }
+
+ @get:Rule val rule = createComposeRule()
+
+ @Test
+ fun simpleDrag() {
+ val draggable = TestDraggable()
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation))
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+ assertThat(draggable.onDragCalled).isFalse()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ var rootCenter = Offset.Zero
+ rule.onRoot().performTouchInput {
+ rootCenter = center
+ down(center)
+ moveBy((touchSlop + 10f).toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragCalled).isTrue()
+ assertThat(draggable.onDragDelta).isEqualTo(10f)
+ assertThat(draggable.onDragStartedPosition).isEqualTo(rootCenter)
+ assertThat(draggable.onDragStartedSign).isEqualTo(1f)
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ rule.onRoot().performTouchInput { moveBy(20f.toOffset()) }
+
+ assertThat(draggable.onDragDelta).isEqualTo(30f)
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ rule.onRoot().performTouchInput {
+ moveBy((-15f).toOffset())
+ up()
+ }
+
+ assertThat(draggable.onDragDelta).isEqualTo(15f)
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
+ @Test
+ fun nestedScrollable() {
+ val draggable = TestDraggable()
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedDraggable(draggable, orientation)
+ .nestedScrollable(rememberScrollState())
+ )
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+ assertThat(draggable.onDragCalled).isFalse()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ var rootCenter = Offset.Zero
+ rule.onRoot().performTouchInput {
+ rootCenter = center
+ down(center)
+ moveBy((-touchSlop - 10f).toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragCalled).isTrue()
+ assertThat(draggable.onDragDelta).isEqualTo(-10f)
+ assertThat(draggable.onDragStartedPosition).isEqualTo(rootCenter)
+ assertThat(draggable.onDragStartedSign).isEqualTo(-1f)
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ rule.onRoot().performTouchInput { moveBy((-20f).toOffset()) }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragCalled).isTrue()
+ assertThat(draggable.onDragDelta).isEqualTo(-30f)
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ rule.onRoot().performTouchInput {
+ moveBy(15f.toOffset())
+ up()
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragCalled).isTrue()
+ assertThat(draggable.onDragDelta).isEqualTo(-15f)
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
+ @Test
+ fun onDragStoppedIsCalledWhenDraggableIsUpdatedAndReset() {
+ val draggable = TestDraggable()
+ var orientation by mutableStateOf(orientation)
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation))
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy(touchSlop.toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ orientation =
+ when (orientation) {
+ Orientation.Horizontal -> Orientation.Vertical
+ Orientation.Vertical -> Orientation.Horizontal
+ }
+ rule.waitForIdle()
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
+ @Test
+ fun onDragStoppedIsCalledWhenDraggableIsUpdatedAndReset_nestedScroll() {
+ val draggable = TestDraggable()
+ var orientation by mutableStateOf(orientation)
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedDraggable(draggable, orientation)
+ .nestedScrollable(rememberScrollState())
+ )
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy((touchSlop + 1f).toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ orientation =
+ when (orientation) {
+ Orientation.Horizontal -> Orientation.Vertical
+ Orientation.Vertical -> Orientation.Horizontal
+ }
+ rule.waitForIdle()
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
+ @Test
+ fun onDragStoppedIsCalledWhenDraggableIsRemovedDuringDrag() {
+ val draggable = TestDraggable()
+ var composeContent by mutableStateOf(true)
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ if (composeContent) {
+ Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation))
+ }
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy(touchSlop.toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ composeContent = false
+ rule.waitForIdle()
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
+ @Test
+ fun onDragStoppedIsCalledWhenDraggableIsRemovedDuringDrag_nestedScroll() {
+ val draggable = TestDraggable()
+ var composeContent by mutableStateOf(true)
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ if (composeContent) {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedDraggable(draggable, orientation)
+ .nestedScrollable(rememberScrollState())
+ )
+ }
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy((touchSlop + 1f).toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ composeContent = false
+ rule.waitForIdle()
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
+ @Test
+ fun onDragStoppedIsCalledWhenDraggableIsRemovedDuringFling() {
+ val draggable = TestDraggable()
+ var composeContent by mutableStateOf(true)
+ var preFlingCalled = false
+ rule.setContent {
+ if (composeContent) {
+ Box(
+ Modifier.fillMaxSize()
+ // This nested scroll connection indefinitely suspends on pre fling, so that
+ // we can emulate what happens when the draggable is removed from
+ // composition while the pre-fling happens and onDragStopped() was not
+ // called yet.
+ .nestedScroll(
+ remember {
+ object : NestedScrollConnection {
+ override suspend fun onPreFling(available: Velocity): Velocity {
+ preFlingCalled = true
+ awaitCancellation()
+ }
+ }
+ }
+ )
+ .nestedDraggable(draggable, orientation)
+ )
+ }
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+
+ // Swipe down.
+ rule.onRoot().performTouchInput {
+ when (orientation) {
+ Orientation.Horizontal -> swipeLeft()
+ Orientation.Vertical -> swipeDown()
+ }
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+ assertThat(preFlingCalled).isTrue()
+
+ composeContent = false
+ rule.waitForIdle()
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
+ @Test
+ @Ignore("b/303224944#comment22")
+ fun onDragStoppedIsCalledWhenNestedScrollableIsRemoved() {
+ val draggable = TestDraggable()
+ var composeNestedScrollable by mutableStateOf(true)
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedDraggable(draggable, orientation)
+ .then(
+ if (composeNestedScrollable) {
+ Modifier.nestedScrollable(rememberScrollState())
+ } else {
+ Modifier
+ }
+ )
+ )
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy((touchSlop + 1f).toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ composeNestedScrollable = false
+ rule.waitForIdle()
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
+ @Test
+ fun enabled() {
+ val draggable = TestDraggable()
+ var enabled by mutableStateOf(false)
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedDraggable(draggable, orientation, enabled = enabled)
+ )
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy(touchSlop.toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ enabled = true
+ rule.onRoot().performTouchInput {
+ // Release previously up finger.
+ up()
+
+ down(center)
+ moveBy(touchSlop.toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ enabled = false
+ rule.waitForIdle()
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
+ private fun ComposeContentTestRule.setContentWithTouchSlop(
+ content: @Composable () -> Unit
+ ): Float {
+ var touchSlop = 0f
+ setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ content()
+ }
+ return touchSlop
+ }
+
+ private fun Modifier.nestedScrollable(scrollState: ScrollState): Modifier {
+ return when (orientation) {
+ Orientation.Vertical -> verticalScroll(scrollState)
+ Orientation.Horizontal -> horizontalScroll(scrollState)
+ }
+ }
+
+ private class TestDraggable(
+ private val onDragStarted: (Offset, Float) -> Unit = { _, _ -> },
+ private val onDrag: (Float) -> Float = { it },
+ private val onDragStopped: suspend (Float) -> Float = { it },
+ private val shouldConsumeNestedScroll: (Float) -> Boolean = { true },
+ ) : NestedDraggable {
+ var onDragStartedCalled = false
+ var onDragCalled = false
+ var onDragStoppedCalled = false
+
+ var onDragStartedPosition = Offset.Zero
+ var onDragStartedSign = 0f
+ var onDragDelta = 0f
+
+ override fun onDragStarted(position: Offset, sign: Float): NestedDraggable.Controller {
+ onDragStartedCalled = true
+ onDragStartedPosition = position
+ onDragStartedSign = sign
+ onDragDelta = 0f
+
+ onDragStarted.invoke(position, sign)
+ return object : NestedDraggable.Controller {
+ override fun onDrag(delta: Float): Float {
+ onDragCalled = true
+ onDragDelta += delta
+ return onDrag.invoke(delta)
+ }
+
+ override suspend fun onDragStopped(velocity: Float): Float {
+ onDragStoppedCalled = true
+ return onDragStopped.invoke(velocity)
+ }
+ }
+ }
+
+ override fun shouldConsumeNestedScroll(sign: Float): Boolean {
+ return shouldConsumeNestedScroll.invoke(sign)
+ }
+ }
+}
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 4705d8d..beaf963 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
@@ -44,7 +44,6 @@
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.observableTransitionState
import com.android.compose.animation.scene.transitions
-import com.android.systemui.Flags.communalHubOnMobile
import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
@@ -188,7 +187,7 @@
scene(
CommunalScenes.Blank,
userActions =
- if (communalHubOnMobile()) emptyMap()
+ if (viewModel.v2FlagEnabled()) emptyMap()
else mapOf(Swipe.Start(fromSource = Edge.End) to CommunalScenes.Communal),
) {
// This scene shows nothing only allowing for transitions to the communal scene.
@@ -198,7 +197,8 @@
scene(
CommunalScenes.Communal,
userActions =
- if (communalHubOnMobile()) emptyMap() else mapOf(Swipe.End to CommunalScenes.Blank),
+ if (viewModel.v2FlagEnabled()) emptyMap()
+ else mapOf(Swipe.End to CommunalScenes.Blank),
) {
CommunalScene(
backgroundType = backgroundType,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 778d7e7..a17a1d4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -23,11 +23,17 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntRect
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.zIndex
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.communal.smartspace.SmartspaceInteractionHandler
import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
import com.android.systemui.communal.ui.compose.section.CommunalPopupSection
+import com.android.systemui.communal.ui.compose.section.CommunalToDreamButtonSection
import com.android.systemui.communal.ui.view.layout.sections.CommunalAppWidgetSection
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
@@ -48,6 +54,7 @@
private val ambientStatusBarSection: AmbientStatusBarSection,
private val communalPopupSection: CommunalPopupSection,
private val widgetSection: CommunalAppWidgetSection,
+ private val communalToDreamButtonSection: CommunalToDreamButtonSection,
) {
@Composable
@@ -59,7 +66,7 @@
Box(modifier = Modifier.fillMaxSize()) {
with(communalPopupSection) { Popup() }
with(ambientStatusBarSection) {
- AmbientStatusBar(modifier = Modifier.fillMaxWidth())
+ AmbientStatusBar(modifier = Modifier.fillMaxWidth().zIndex(1f))
}
CommunalHub(
viewModel = viewModel,
@@ -81,11 +88,13 @@
Modifier.element(Communal.Elements.IndicationArea).fillMaxWidth()
)
}
+ with(communalToDreamButtonSection) { Button() }
},
) { measurables, constraints ->
val communalGridMeasurable = measurables[0]
val lockIconMeasurable = measurables[1]
val bottomAreaMeasurable = measurables[2]
+ val screensaverButtonMeasurable: Measurable? = measurables.getOrNull(3)
val noMinConstraints = constraints.copy(minWidth = 0, minHeight = 0)
@@ -100,6 +109,15 @@
val bottomAreaPlaceable = bottomAreaMeasurable.measure(noMinConstraints)
+ val screensaverButtonSizeInt = screensaverButtonSize.roundToPx()
+ val screensaverButtonPlaceable =
+ screensaverButtonMeasurable?.measure(
+ Constraints.fixed(
+ width = screensaverButtonSizeInt,
+ height = screensaverButtonSizeInt,
+ )
+ )
+
val communalGridPlaceable =
communalGridMeasurable.measure(
noMinConstraints.copy(maxHeight = lockIconBounds.top)
@@ -108,12 +126,22 @@
layout(constraints.maxWidth, constraints.maxHeight) {
communalGridPlaceable.place(x = 0, y = 0)
lockIconPlaceable.place(x = lockIconBounds.left, y = lockIconBounds.top)
- bottomAreaPlaceable.place(
- x = 0,
- y = constraints.maxHeight - bottomAreaPlaceable.height,
+
+ val bottomAreaTop = constraints.maxHeight - bottomAreaPlaceable.height
+ bottomAreaPlaceable.place(x = 0, y = bottomAreaTop)
+ screensaverButtonPlaceable?.place(
+ x =
+ constraints.maxWidth -
+ screensaverButtonSizeInt -
+ Dimensions.ItemSpacing.roundToPx(),
+ y = lockIconBounds.top,
)
}
}
}
}
+
+ companion object {
+ val screensaverButtonSize: Dp = 64.dp
+ }
}
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 573e5ca..a55b6d7 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
@@ -187,6 +187,7 @@
import com.android.systemui.communal.ui.viewmodel.ResizeInfo
import com.android.systemui.communal.ui.viewmodel.ResizeableItemFrameViewModel
import com.android.systemui.communal.util.DensityUtils.Companion.adjustedDp
+import com.android.systemui.communal.util.ResizeUtils.resizeOngoingItems
import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.lifecycle.rememberViewModel
@@ -217,6 +218,7 @@
var removeButtonCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
var toolbarSize: IntSize? by remember { mutableStateOf(null) }
var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
+ var contentOffset: Offset by remember { mutableStateOf(Offset.Zero) }
val gridState =
rememberLazyGridState(viewModel.savedFirstScrollIndex, viewModel.savedFirstScrollOffset)
@@ -239,9 +241,7 @@
initialValue = !viewModel.isEditMode
)
- val contentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize)
- val contentOffset = beforeContentPadding(contentPadding).toOffset()
-
+ val minContentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize)
ObserveScrollEffect(gridState, viewModel)
val context = LocalContext.current
@@ -367,7 +367,7 @@
) {
AccessibilityContainer(viewModel) {
if (!viewModel.isEditMode && isEmptyState) {
- EmptyStateCta(contentPadding = contentPadding, viewModel = viewModel)
+ EmptyStateCta(contentPadding = minContentPadding, viewModel = viewModel)
} else {
val slideOffsetInPx =
with(LocalDensity.current) { Dimensions.SlideOffsetY.toPx().toInt() }
@@ -396,10 +396,11 @@
CommunalHubLazyGrid(
communalContent = communalContent,
viewModel = viewModel,
- contentPadding = contentPadding,
+ minContentPadding = minContentPadding,
contentOffset = contentOffset,
screenWidth = screenWidth,
setGridCoordinates = { gridCoordinates = it },
+ setContentOffset = { contentOffset = it },
updateDragPositionForRemove = { boundingBox ->
val gridOffset = gridCoordinates?.positionInWindow()
val removeButtonCenter =
@@ -741,8 +742,9 @@
@Composable
private fun HorizontalGridWrapper(
- contentPadding: PaddingValues,
+ minContentPadding: PaddingValues,
gridState: LazyGridState,
+ setContentOffset: (offset: Offset) -> Unit,
modifier: Modifier = Modifier,
content: LazyGridScope.(sizeInfo: SizeInfo?) -> Unit,
) {
@@ -751,17 +753,26 @@
cellAspectRatio = 1.5f,
modifier = modifier,
state = gridState,
- minContentPadding = contentPadding,
+ minContentPadding = minContentPadding,
minHorizontalArrangement = Dimensions.ItemSpacing,
minVerticalArrangement = Dimensions.ItemSpacing,
+ setContentOffset = setContentOffset,
content = content,
)
} else {
+ val layoutDirection = LocalLayoutDirection.current
+ val density = LocalDensity.current
+
+ val minStartPadding = minContentPadding.calculateStartPadding(layoutDirection)
+ val minTopPadding = minContentPadding.calculateTopPadding()
+
+ with(density) { setContentOffset(Offset(minStartPadding.toPx(), minTopPadding.toPx())) }
+
LazyHorizontalGrid(
modifier = modifier,
state = gridState,
rows = GridCells.Fixed(CommunalContentSize.FixedSize.FULL.span),
- contentPadding = contentPadding,
+ contentPadding = minContentPadding,
horizontalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing),
verticalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing),
) {
@@ -775,13 +786,14 @@
private fun BoxScope.CommunalHubLazyGrid(
communalContent: List<CommunalContentModel>,
viewModel: BaseCommunalViewModel,
- contentPadding: PaddingValues,
+ minContentPadding: PaddingValues,
selectedKey: State<String?>,
screenWidth: Int,
contentOffset: Offset,
gridState: LazyGridState,
contentListState: ContentListState,
setGridCoordinates: (coordinates: LayoutCoordinates) -> Unit,
+ setContentOffset: (offset: Offset) -> Unit,
updateDragPositionForRemove: (boundingBox: IntRect) -> Boolean,
widgetConfigurator: WidgetConfigurator?,
interactionHandler: RemoteViews.InteractionHandler?,
@@ -832,10 +844,19 @@
HorizontalGridWrapper(
modifier = gridModifier,
gridState = gridState,
- contentPadding = contentPadding,
+ minContentPadding = minContentPadding,
+ setContentOffset = setContentOffset,
) { sizeInfo ->
+ /** Override spans based on the responsive grid size */
+ val finalizedList =
+ if (sizeInfo != null) {
+ resizeOngoingItems(list, sizeInfo.gridSize.height)
+ } else {
+ list
+ }
+
itemsIndexed(
- items = list,
+ items = finalizedList,
key = { _, item -> item.key },
contentType = { _, item -> item.key },
span = { _, item -> GridItemSpan(item.getSpanOrMax(sizeInfo?.gridSize?.height)) },
@@ -883,7 +904,7 @@
key = item.key,
currentSpan = GridItemSpan(currentItemSpan),
gridState = gridState,
- gridContentPadding = contentPadding,
+ gridContentPadding = sizeInfo?.contentPadding ?: minContentPadding,
verticalArrangement =
Arrangement.spacedBy(
sizeInfo?.verticalArrangement ?: Dimensions.ItemSpacing
@@ -1193,6 +1214,7 @@
is CommunalContentModel.Smartspace -> SmartspaceContent(interactionHandler, model, modifier)
is CommunalContentModel.Tutorial -> TutorialContent(modifier)
is CommunalContentModel.Umo -> Umo(viewModel, sceneScope, modifier)
+ is CommunalContentModel.Spacer -> Box(Modifier.fillMaxSize())
}
}
@@ -1723,24 +1745,22 @@
val windowMetrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context)
val screenHeight = with(density) { windowMetrics.bounds.height().toDp() }
val toolbarHeight = with(density) { Dimensions.ToolbarPaddingTop + toolbarSize.height.toDp() }
- val verticalPadding =
- ((screenHeight - toolbarHeight - hubDimensions.GridHeight + hubDimensions.GridTopSpacing) /
- 2)
- .coerceAtLeast(Dimensions.Spacing)
- return PaddingValues(
- start = Dimensions.ToolbarPaddingHorizontal,
- end = Dimensions.ToolbarPaddingHorizontal,
- top = verticalPadding + toolbarHeight,
- bottom = verticalPadding,
- )
-}
-
-@Composable
-private fun beforeContentPadding(paddingValues: PaddingValues): ContentPaddingInPx {
- return with(LocalDensity.current) {
- ContentPaddingInPx(
- start = paddingValues.calculateStartPadding(LocalLayoutDirection.current).toPx(),
- top = paddingValues.calculateTopPadding().toPx(),
+ return if (communalResponsiveGrid()) {
+ PaddingValues(
+ start = Dimensions.ToolbarPaddingHorizontal,
+ end = Dimensions.ToolbarPaddingHorizontal,
+ top = hubDimensions.GridTopSpacing,
+ )
+ } else {
+ val verticalPadding =
+ ((screenHeight - toolbarHeight - hubDimensions.GridHeight +
+ hubDimensions.GridTopSpacing) / 2)
+ .coerceAtLeast(Dimensions.Spacing)
+ PaddingValues(
+ start = Dimensions.ToolbarPaddingHorizontal,
+ end = Dimensions.ToolbarPaddingHorizontal,
+ top = verticalPadding + toolbarHeight,
+ bottom = verticalPadding,
)
}
}
@@ -1760,10 +1780,6 @@
private fun keyAtIndexIfEditable(list: List<CommunalContentModel>, index: Int): String? =
if (index in list.indices && list[index].isWidgetContent()) list[index].key else null
-data class ContentPaddingInPx(val start: Float, val top: Float) {
- fun toOffset(): Offset = Offset(start, top)
-}
-
class Dimensions(val context: Context, val config: Configuration) {
val GridTopSpacing: Dp
get() {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
index 7a50080..ef5e90b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
@@ -94,7 +94,7 @@
private val scope: CoroutineScope,
private val updateDragPositionForRemove: (draggingBoundingBox: IntRect) -> Boolean,
) {
- var draggingItemKey by mutableStateOf<String?>(null)
+ var draggingItemKey by mutableStateOf<Any?>(null)
private set
var isDraggingToRemove by mutableStateOf(false)
@@ -138,7 +138,7 @@
// before content padding from the initial pointer position
.firstItemAtOffset(normalizedOffset - contentOffset)
?.apply {
- draggingItemKey = key as String
+ draggingItemKey = key
draggingItemInitialOffset = this.offset.toOffset()
return true
}
@@ -284,9 +284,7 @@
contentOffset,
)
) {
- // draggingItemKey is guaranteed to be non-null here because it is set in
- // onDragStart()
- viewModel.onReorderWidgetStart(dragDropState.draggingItemKey!!)
+ viewModel.onReorderWidgetStart()
}
},
onDragEnd = {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
index 3642127..21b3474 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
@@ -35,7 +35,9 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
@@ -54,6 +56,7 @@
cellAspectRatio: Float,
modifier: Modifier = Modifier,
state: LazyGridState = rememberLazyGridState(),
+ setContentOffset: (offset: Offset) -> Unit = {},
minContentPadding: PaddingValues = PaddingValues(0.dp),
minHorizontalArrangement: Dp = 0.dp,
minVerticalArrangement: Dp = 0.dp,
@@ -70,6 +73,7 @@
BoxWithConstraints(modifier) {
val gridSize = rememberGridSize(maxWidth = maxWidth, maxHeight = maxHeight)
val layoutDirection = LocalLayoutDirection.current
+ val density = LocalDensity.current
val minStartPadding = minContentPadding.calculateStartPadding(layoutDirection)
val minEndPadding = minContentPadding.calculateEndPadding(layoutDirection)
@@ -124,14 +128,19 @@
val extraWidth = maxWidth - usedWidth
val extraHeight = maxHeight - usedHeight
+ val finalStartPadding = minStartPadding + extraWidth / 2
+ val finalTopPadding = minTopPadding + extraHeight / 2
+
val finalContentPadding =
PaddingValues(
- start = minStartPadding + extraWidth / 2,
+ start = finalStartPadding,
end = minEndPadding + extraWidth / 2,
- top = minTopPadding + extraHeight / 2,
+ top = finalTopPadding,
bottom = minBottomPadding + extraHeight / 2,
)
+ with(density) { setContentOffset(Offset(finalStartPadding.toPx(), finalTopPadding.toPx())) }
+
LazyHorizontalGrid(
rows = GridCells.Fixed(gridSize.height),
modifier = Modifier.fillMaxSize(),
@@ -179,12 +188,13 @@
* @property verticalArrangement The space between rows in the grid.
* @property gridSize The size of the grid, in cell units.
* @property availableHeight The maximum height an item in the grid may occupy.
+ * @property contentPadding The padding around the content of the grid.
*/
data class SizeInfo(
val cellSize: DpSize,
val verticalArrangement: Dp,
val gridSize: IntSize,
- private val contentPadding: PaddingValues,
+ val contentPadding: PaddingValues,
private val maxHeight: Dp,
) {
val availableHeight: Dp
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt
new file mode 100644
index 0000000..9421596
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.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.communal.ui.compose.section
+
+import androidx.compose.material3.IconButtonDefaults
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.res.stringResource
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.PlatformIconButton
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.ui.viewmodel.CommunalToDreamButtonViewModel
+import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+class CommunalToDreamButtonSection
+@Inject
+constructor(
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
+ private val viewModelFactory: CommunalToDreamButtonViewModel.Factory,
+) {
+ @Composable
+ fun Button() {
+ if (!communalSettingsInteractor.isV2FlagEnabled()) {
+ return
+ }
+
+ val viewModel =
+ rememberViewModel("CommunalToDreamButtonSection") { viewModelFactory.create() }
+ val shouldShowDreamButtonOnHub by
+ viewModel.shouldShowDreamButtonOnHub.collectAsStateWithLifecycle(false)
+
+ if (!shouldShowDreamButtonOnHub) {
+ return
+ }
+
+ PlatformIconButton(
+ onClick = { viewModel.onShowDreamButtonTap() },
+ iconResource = R.drawable.ic_screensaver_auto,
+ contentDescription =
+ stringResource(R.string.accessibility_glanceable_hub_to_dream_button),
+ colors =
+ IconButtonDefaults.filledIconButtonColors(
+ contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ containerColor = MaterialTheme.colorScheme.primaryContainer,
+ ),
+ )
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
index 1475795..d022150 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
@@ -18,6 +18,7 @@
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.Crossfade
+import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
@@ -29,7 +30,9 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -60,13 +63,25 @@
@Composable
fun AlternateBouncer(
alternateBouncerDependencies: AlternateBouncerDependencies,
+ onHideAnimationFinished: () -> Unit,
modifier: Modifier = Modifier,
) {
val isVisible by
- alternateBouncerDependencies.viewModel.isVisible.collectAsStateWithLifecycle(
- initialValue = false
- )
+ alternateBouncerDependencies.viewModel.isVisible.collectAsStateWithLifecycle(true)
+ val visibleState = remember { MutableTransitionState(isVisible) }
+
+ // Feeds the isVisible value to the MutableTransitionState used by AnimatedVisibility below.
+ LaunchedEffect(isVisible) { visibleState.targetState = isVisible }
+
+ // Watches the MutableTransitionState and calls onHideAnimationFinished when the fade out
+ // animation is finished. This way the window view is removed from the view hierarchy only after
+ // the fade out animation is complete.
+ LaunchedEffect(visibleState.currentState, visibleState.isIdle) {
+ if (!visibleState.currentState && visibleState.isIdle) {
+ onHideAnimationFinished()
+ }
+ }
val udfpsIconLocation by
alternateBouncerDependencies.udfpsIconViewModel.iconLocation.collectAsStateWithLifecycle(
@@ -74,7 +89,7 @@
)
AnimatedVisibility(
- visible = isVisible,
+ visibleState = visibleState,
enter = fadeIn(),
exit = fadeOut(),
modifier = modifier,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
index da78eed..1cee4d6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
@@ -77,7 +77,7 @@
resources.getDimensionPixelSize(
R.dimen.keyguard_status_view_bottom_margin
)
- }
+ },
)
) {
if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) {
@@ -87,6 +87,7 @@
val paddingBelowClockStart =
dimensionResource(R.dimen.below_clock_padding_start)
val paddingBelowClockEnd = dimensionResource(R.dimen.below_clock_padding_end)
+ val paddingCardHorizontal = paddingBelowClockEnd
if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) {
Row(
@@ -96,16 +97,14 @@
// All items will be constrained to be as tall as the shortest
// item.
.height(IntrinsicSize.Min)
- .padding(
- start = paddingBelowClockStart,
- ),
+ .padding(start = paddingBelowClockStart),
) {
Date(
modifier =
Modifier.burnInAware(
viewModel = aodBurnInViewModel,
params = burnInParams,
- ),
+ )
)
Spacer(modifier = Modifier.width(4.dp))
Weather(
@@ -113,7 +112,7 @@
Modifier.burnInAware(
viewModel = aodBurnInViewModel,
params = burnInParams,
- ),
+ )
)
}
}
@@ -121,14 +120,8 @@
Card(
modifier =
Modifier.fillMaxWidth()
- .padding(
- start = paddingBelowClockStart,
- end = paddingBelowClockEnd,
- )
- .burnInAware(
- viewModel = aodBurnInViewModel,
- params = burnInParams,
- ),
+ .padding(start = paddingCardHorizontal, end = paddingCardHorizontal)
+ .burnInAware(viewModel = aodBurnInViewModel, params = burnInParams)
)
}
}
@@ -136,9 +129,7 @@
}
@Composable
- private fun Card(
- modifier: Modifier = Modifier,
- ) {
+ private fun Card(modifier: Modifier = Modifier) {
AndroidView(
factory = { context ->
FrameLayout(context).apply {
@@ -161,9 +152,7 @@
}
@Composable
- private fun Weather(
- modifier: Modifier = Modifier,
- ) {
+ private fun Weather(modifier: Modifier = Modifier) {
val isVisible by keyguardSmartspaceViewModel.isWeatherVisible.collectAsStateWithLifecycle()
if (!isVisible) {
return
@@ -188,9 +177,7 @@
}
@Composable
- private fun Date(
- modifier: Modifier = Modifier,
- ) {
+ private fun Date(modifier: Modifier = Modifier) {
val isVisible by keyguardSmartspaceViewModel.isDateVisible.collectAsStateWithLifecycle()
if (!isVisible) {
return
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
index 4f1acdc..6591a75 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
@@ -27,6 +27,7 @@
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.safeContentPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
@@ -95,7 +96,9 @@
recentTiles: List<PeopleTileViewModel>,
onTileClicked: (PeopleTileViewModel) -> Unit,
) {
- Column(Modifier.sysuiResTag("top_level_with_conversations")) {
+ Column(
+ Modifier.fillMaxSize().safeContentPadding().sysuiResTag("top_level_with_conversations")
+ ) {
Column(
Modifier.fillMaxWidth().padding(PeopleSpacePadding),
horizontalAlignment = Alignment.CenterHorizontally,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt
index d483f88..9f582bc 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt
@@ -27,6 +27,7 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.safeContentPadding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
@@ -47,7 +48,7 @@
@Composable
internal fun PeopleScreenEmpty(onGotItClicked: () -> Unit) {
Column(
- Modifier.fillMaxSize().padding(PeopleSpacePadding),
+ Modifier.fillMaxSize().safeContentPadding().padding(PeopleSpacePadding),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 2d32fd7..f7ce215 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -261,7 +261,7 @@
/** A button with an icon. */
@Composable
-private fun IconButton(model: FooterActionsButtonViewModel, modifier: Modifier = Modifier) {
+fun IconButton(model: FooterActionsButtonViewModel, modifier: Modifier = Modifier) {
Expandable(
color = colorAttr(model.backgroundColor),
shape = CircleShape,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
index 3fce890..b1a1945 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
@@ -26,8 +26,10 @@
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.requiredHeightIn
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
@@ -50,6 +52,8 @@
import com.android.systemui.qs.panels.ui.compose.EditMode
import com.android.systemui.qs.panels.ui.compose.TileDetails
import com.android.systemui.qs.panels.ui.compose.TileGrid
+import com.android.systemui.qs.panels.ui.compose.toolbar.Toolbar
+import com.android.systemui.qs.ui.composable.QuickSettingsShade.Dimensions.GridMaxHeight
import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel
import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeOverlayActionsViewModel
import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeOverlayContentViewModel
@@ -122,7 +126,9 @@
// A sealed interface to represent the possible states of the `ShadeBody`
sealed interface ShadeBodyState {
data object Editing : ShadeBodyState
+
data object TileDetails : ShadeBodyState
+
data object Default : ShadeBodyState
}
@@ -149,9 +155,8 @@
ShadeBodyState.Editing -> {
EditMode(
viewModel = viewModel.editModeViewModel,
- modifier = Modifier
- .fillMaxWidth()
- .padding(QuickSettingsShade.Dimensions.Padding),
+ modifier =
+ Modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding),
)
}
ShadeBodyState.TileDetails -> {
@@ -182,22 +187,23 @@
.padding(
start = QuickSettingsShade.Dimensions.Padding,
end = QuickSettingsShade.Dimensions.Padding,
- top = QuickSettingsShade.Dimensions.Padding,
+ bottom = QuickSettingsShade.Dimensions.Padding / 2,
),
) {
+ Toolbar(viewModel.toolbarViewModelFactory)
BrightnessSliderContainer(
viewModel = viewModel.brightnessSliderViewModel,
modifier =
Modifier.fillMaxWidth().height(QuickSettingsShade.Dimensions.BrightnessSliderHeight),
)
- Box {
+ Box(
+ modifier =
+ Modifier.requiredHeightIn(max = GridMaxHeight)
+ .verticalNestedScrollToScene()
+ .verticalScroll(rememberScrollState())
+ ) {
GridAnchor()
- TileGrid(
- viewModel = viewModel.tileGridViewModel,
- modifier =
- Modifier.fillMaxWidth()
- .heightIn(max = QuickSettingsShade.Dimensions.GridMaxHeight),
- )
+ TileGrid(viewModel = viewModel.tileGridViewModel, modifier = Modifier.fillMaxWidth())
}
}
}
@@ -207,6 +213,6 @@
object Dimensions {
val Padding = 16.dp
val BrightnessSliderHeight = 64.dp
- val GridMaxHeight = 800.dp
+ val GridMaxHeight = 420.dp
}
}
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 6d03118..0e35e1d 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
@@ -45,6 +45,7 @@
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.Expandable
+import com.android.systemui.Flags
import com.android.systemui.animation.Expandable
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
@@ -56,7 +57,7 @@
/** [ComposeVolumePanelUiComponent] implementing a clickable button from a bottom row. */
class ButtonComponent(
private val viewModelFlow: StateFlow<ButtonViewModel?>,
- private val onClick: (expandable: Expandable, horizontalGravity: Int) -> Unit
+ private val onClick: (expandable: Expandable, horizontalGravity: Int) -> Unit,
) : ComposeVolumePanelUiComponent {
@Composable
@@ -84,14 +85,26 @@
},
color =
if (viewModel.isActive) {
- MaterialTheme.colorScheme.tertiaryContainer
+ if (Flags.volumeRedesign()) {
+ MaterialTheme.colorScheme.primary
+ } else {
+ MaterialTheme.colorScheme.tertiaryContainer
+ }
} else {
- MaterialTheme.colorScheme.surface
+ if (Flags.volumeRedesign()) {
+ MaterialTheme.colorScheme.surfaceContainerHigh
+ } else {
+ MaterialTheme.colorScheme.surface
+ }
},
shape = RoundedCornerShape(20.dp),
contentColor =
if (viewModel.isActive) {
- MaterialTheme.colorScheme.onTertiaryContainer
+ if (Flags.volumeRedesign()) {
+ MaterialTheme.colorScheme.onPrimary
+ } else {
+ MaterialTheme.colorScheme.onTertiaryContainer
+ }
} else {
MaterialTheme.colorScheme.onSurface
},
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 bb2daec..2cd7304 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
@@ -42,6 +42,7 @@
import androidx.compose.ui.state.ToggleableState
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.systemui.Flags
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
@@ -51,7 +52,7 @@
/** [ComposeVolumePanelUiComponent] implementing a toggleable button from a bottom row. */
class ToggleButtonComponent(
private val viewModelFlow: StateFlow<ButtonViewModel?>,
- private val onCheckedChange: (isChecked: Boolean) -> Unit
+ private val onCheckedChange: (isChecked: Boolean) -> Unit,
) : ComposeVolumePanelUiComponent {
@Composable
@@ -68,15 +69,29 @@
BottomComponentButtonSurface {
val colors =
if (viewModel.isActive) {
- ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.tertiaryContainer,
- contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
- )
+ if (Flags.volumeRedesign()) {
+ ButtonDefaults.buttonColors(
+ containerColor = MaterialTheme.colorScheme.primary,
+ contentColor = MaterialTheme.colorScheme.onPrimary,
+ )
+ } else {
+ ButtonDefaults.buttonColors(
+ containerColor = MaterialTheme.colorScheme.tertiaryContainer,
+ contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
+ )
+ }
} else {
- ButtonDefaults.buttonColors(
- containerColor = Color.Transparent,
- contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
- )
+ if (Flags.volumeRedesign()) {
+ ButtonDefaults.buttonColors(
+ containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
+ contentColor = MaterialTheme.colorScheme.onSurface,
+ )
+ } else {
+ ButtonDefaults.buttonColors(
+ containerColor = Color.Transparent,
+ contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
+ }
}
Button(
modifier =
@@ -93,7 +108,7 @@
onClick = { onCheckedChange(!viewModel.isActive) },
shape = RoundedCornerShape(20.dp),
colors = colors,
- contentPadding = PaddingValues(0.dp)
+ contentPadding = PaddingValues(0.dp),
) {
Icon(modifier = Modifier.size(24.dp), icon = viewModel.icon)
}
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 581fb9d..25892c5 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
@@ -37,11 +37,13 @@
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
+import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
@@ -51,8 +53,11 @@
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.PlatformIconButton
import com.android.compose.PlatformSliderColors
import com.android.compose.modifiers.padding
+import com.android.compose.modifiers.thenIf
+import com.android.systemui.Flags
import com.android.systemui.res.R
import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderViewModel
@@ -84,7 +89,11 @@
val sliderPadding by topSliderPadding(isExpandable)
VolumeSlider(
- modifier = Modifier.padding(end = { sliderPadding.roundToPx() }).fillMaxWidth(),
+ modifier =
+ Modifier.thenIf(!Flags.volumeRedesign()) {
+ Modifier.padding(end = { sliderPadding.roundToPx() })
+ }
+ .fillMaxWidth(),
state = sliderState,
onValueChange = { newValue: Float ->
sliderViewModel.onValueChanged(sliderState, newValue)
@@ -93,15 +102,29 @@
onIconTapped = { sliderViewModel.toggleMuted(sliderState) },
sliderColors = sliderColors,
hapticsViewModelFactory = sliderViewModel.getSliderHapticsViewModelFactory(),
+ button =
+ if (Flags.volumeRedesign()) {
+ {
+ ExpandButton(
+ isExpanded = isExpanded,
+ isExpandable = isExpandable,
+ onExpandedChanged = onExpandedChanged,
+ )
+ }
+ } else {
+ null
+ },
)
- ExpandButton(
- modifier = Modifier.align(Alignment.CenterEnd),
- isExpanded = isExpanded,
- isExpandable = isExpandable,
- onExpandedChanged = onExpandedChanged,
- sliderColors = sliderColors,
- )
+ if (!Flags.volumeRedesign()) {
+ ExpandButtonLegacy(
+ modifier = Modifier.align(Alignment.CenterEnd),
+ isExpanded = isExpanded,
+ isExpandable = isExpandable,
+ onExpandedChanged = onExpandedChanged,
+ sliderColors = sliderColors,
+ )
+ }
}
AnimatedVisibility(
visible = isExpanded || !isExpandable,
@@ -153,7 +176,7 @@
}
@Composable
-private fun ExpandButton(
+private fun ExpandButtonLegacy(
isExpanded: Boolean,
isExpandable: Boolean,
onExpandedChanged: (Boolean) -> Unit,
@@ -200,6 +223,48 @@
}
}
+@Composable
+private fun ExpandButton(
+ isExpanded: Boolean,
+ isExpandable: Boolean,
+ onExpandedChanged: (Boolean) -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ val expandButtonStateDescription =
+ if (isExpanded) {
+ stringResource(R.string.volume_panel_expanded_sliders)
+ } else {
+ stringResource(R.string.volume_panel_collapsed_sliders)
+ }
+ AnimatedVisibility(
+ modifier = modifier,
+ visible = isExpandable,
+ enter = expandButtonEnterTransition(),
+ exit = expandButtonExitTransition(),
+ ) {
+ PlatformIconButton(
+ modifier =
+ Modifier.size(width = 48.dp, height = 40.dp).semantics {
+ role = Role.Switch
+ stateDescription = expandButtonStateDescription
+ },
+ onClick = { onExpandedChanged(!isExpanded) },
+ colors =
+ IconButtonDefaults.iconButtonColors(
+ containerColor = Color.Transparent,
+ contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
+ ),
+ iconResource =
+ if (isExpanded) {
+ R.drawable.ic_arrow_down_24dp
+ } else {
+ R.drawable.ic_arrow_up_24dp
+ },
+ contentDescription = null,
+ )
+ }
+}
+
private fun enterTransition(index: Int, totalCount: Int): EnterTransition {
val enterDelay = ((totalCount - index + 1) * 10).coerceAtLeast(0)
val enterDuration = (EXPAND_DURATION_MILLIS - enterDelay).coerceAtLeast(100)
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 97ce429..fa5f72b 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
@@ -24,9 +24,18 @@
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Slider
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
@@ -48,6 +57,7 @@
import androidx.compose.ui.unit.dp
import com.android.compose.PlatformSlider
import com.android.compose.PlatformSliderColors
+import com.android.systemui.Flags
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.compose.modifiers.sysuiResTag
@@ -61,11 +71,104 @@
fun VolumeSlider(
state: SliderState,
onValueChange: (newValue: Float) -> Unit,
- onValueChangeFinished: (() -> Unit)? = null,
onIconTapped: () -> Unit,
+ sliderColors: PlatformSliderColors,
modifier: Modifier = Modifier,
+ hapticsViewModelFactory: SliderHapticsViewModel.Factory?,
+ onValueChangeFinished: (() -> Unit)? = null,
+ button: (@Composable () -> Unit)? = null,
+) {
+ if (!Flags.volumeRedesign()) {
+ LegacyVolumeSlider(
+ state = state,
+ onValueChange = onValueChange,
+ onIconTapped = onIconTapped,
+ sliderColors = sliderColors,
+ onValueChangeFinished = onValueChangeFinished,
+ modifier = modifier,
+ hapticsViewModelFactory = hapticsViewModelFactory,
+ )
+ return
+ }
+
+ val value by valueState(state)
+ Column(modifier) {
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(12.dp),
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ state.icon?.let {
+ Icon(
+ icon = it,
+ tint = MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier.size(40.dp).padding(8.dp),
+ )
+ }
+ Text(
+ text = state.label,
+ style = MaterialTheme.typography.titleMedium,
+ color = MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier.weight(1f).align(Alignment.CenterVertically),
+ )
+ button?.invoke()
+ }
+ Slider(
+ value = value,
+ valueRange = state.valueRange,
+ onValueChange = onValueChange,
+ onValueChangeFinished = onValueChangeFinished,
+ enabled = state.isEnabled,
+ modifier =
+ Modifier.height(40.dp).sysuiResTag(state.label).clearAndSetSemantics {
+ if (state.isEnabled) {
+ contentDescription = state.label
+ state.a11yClickDescription?.let {
+ customActions =
+ listOf(
+ CustomAccessibilityAction(it) {
+ onIconTapped()
+ true
+ }
+ )
+ }
+
+ state.a11yStateDescription?.let { stateDescription = it }
+ progressBarRangeInfo = ProgressBarRangeInfo(state.value, state.valueRange)
+ } else {
+ disabled()
+ contentDescription =
+ state.disabledMessage?.let { "${state.label}, $it" } ?: state.label
+ }
+ setProgress { targetValue ->
+ val targetDirection =
+ when {
+ targetValue > value -> 1
+ targetValue < value -> -1
+ else -> 0
+ }
+
+ val newValue =
+ (value + targetDirection * state.a11yStep).coerceIn(
+ state.valueRange.start,
+ state.valueRange.endInclusive,
+ )
+ onValueChange(newValue)
+ true
+ }
+ },
+ )
+ }
+}
+
+@Composable
+private fun LegacyVolumeSlider(
+ state: SliderState,
+ onValueChange: (newValue: Float) -> Unit,
+ onIconTapped: () -> Unit,
sliderColors: PlatformSliderColors,
hapticsViewModelFactory: SliderHapticsViewModel.Factory?,
+ modifier: Modifier = Modifier,
+ onValueChangeFinished: (() -> Unit)? = null,
) {
val value by valueState(state)
val interactionSource = remember { MutableInteractionSource() }
@@ -178,7 +281,7 @@
val shouldSkipAnimation =
prevState is SliderState.Empty || prevState.isEnabled != state.isEnabled
val value =
- if (shouldSkipAnimation) mutableFloatStateOf(state.value)
+ if (shouldSkipAnimation) remember { mutableFloatStateOf(state.value) }
else animateFloatAsState(targetValue = state.value, label = "VolumeSliderValueAnimation")
prevState = state
return value
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSliderContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSliderContent.kt
index 4ae4eb8..28226ff 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSliderContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSliderContent.kt
@@ -53,7 +53,7 @@
DisabledMessage,
}
-/** Shows label of the [VolumeSlider]. Also shows [disabledMessage] when not [isEnabled]. */
+/** Shows label of the [LegacyVolumeSlider]. Also shows [disabledMessage] when not [isEnabled]. */
@Composable
fun VolumeSliderContent(
label: String,
@@ -89,7 +89,7 @@
}
}
},
- measurePolicy = VolumeSliderContentMeasurePolicy(isEnabled)
+ measurePolicy = VolumeSliderContentMeasurePolicy(isEnabled),
)
}
@@ -102,7 +102,7 @@
override fun MeasureScope.measure(
measurables: List<Measurable>,
- constraints: Constraints
+ constraints: Constraints,
): MeasureResult {
val labelPlaceable =
measurables
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
index 0fc88b2..a4237f3 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
@@ -225,7 +225,7 @@
return animateElementValueAsState(value, key, SharedColorType, canOverflow = false)
}
-private object SharedColorType : SharedValueType<Color, ColorDelta> {
+internal object SharedColorType : SharedValueType<Color, ColorDelta> {
override val unspecifiedValue: Color = Color.Unspecified
override val zeroDeltaValue: ColorDelta = ColorDelta(0f, 0f, 0f, 0f)
@@ -255,17 +255,17 @@
alpha = aOklab.alpha + b.alpha * bWeight,
colorSpace = ColorSpaces.Oklab,
)
- .convert(aOklab.colorSpace)
+ .convert(a.colorSpace)
}
}
/**
- * Represents the diff between two colors in the same color space.
+ * Represents the diff between two colors in the Oklab color space.
*
* Note: This class is necessary because Color() checks the bounds of its values and UncheckedColor
* is internal.
*/
-private class ColorDelta(val red: Float, val green: Float, val blue: Float, val alpha: Float)
+internal class ColorDelta(val red: Float, val green: Float, val blue: Float, val alpha: Float)
@Composable
internal fun <T> animateSharedValueAsState(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 7a8d20a..2d589f3 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -27,8 +27,11 @@
import com.android.compose.nestedscroll.OnStopScope
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
import com.android.compose.nestedscroll.ScrollController
+import com.android.compose.ui.util.SpaceVectorConverter
import kotlin.math.absoluteValue
+import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
internal interface DraggableHandler {
/**
@@ -124,7 +127,7 @@
return newDragController
}
- internal fun createSwipeAnimation(swipes: Swipes, result: UserActionResult): SwipeAnimation<*> {
+ private fun createSwipeAnimation(swipes: Swipes, result: UserActionResult): SwipeAnimation<*> {
val upOrLeftResult = swipes.upOrLeftResult
val downOrRightResult = swipes.downOrRightResult
val isUpOrLeft =
@@ -191,9 +194,15 @@
private val draggableHandler: DraggableHandlerImpl,
val swipes: Swipes,
var swipeAnimation: SwipeAnimation<*>,
-) : DragController {
+) : DragController, SpaceVectorConverter by SpaceVectorConverter(draggableHandler.orientation) {
val layoutState = draggableHandler.layoutImpl.state
+ val overscrollableContent: OverscrollableContent =
+ when (draggableHandler.orientation) {
+ Orientation.Vertical -> draggableHandler.layoutImpl.verticalOverscrollableContent
+ Orientation.Horizontal -> draggableHandler.layoutImpl.horizontalOverscrollableContent
+ }
+
/**
* Whether this handle is active. If this returns false, calling [onDrag] and [onStop] will do
* nothing.
@@ -224,66 +233,75 @@
* @return the consumed delta
*/
override fun onDrag(delta: Float): Float {
- return onDrag(delta, swipeAnimation)
- }
-
- private fun <T : ContentKey> onDrag(delta: Float, swipeAnimation: SwipeAnimation<T>): Float {
- if (delta == 0f || !isDrivingTransition || swipeAnimation.isAnimatingOffset()) {
+ val initialAnimation = swipeAnimation
+ if (delta == 0f || !isDrivingTransition || initialAnimation.isAnimatingOffset()) {
return 0f
}
+ // swipeAnimation can change during the gesture, we want to always use the initial reference
+ // during the whole drag gesture.
+ return dragWithOverscroll(delta, animation = initialAnimation)
+ }
- val distance = swipeAnimation.distance()
- val previousOffset = swipeAnimation.dragOffset
+ private fun <T : ContentKey> dragWithOverscroll(
+ delta: Float,
+ animation: SwipeAnimation<T>,
+ ): Float {
+ require(delta != 0f) { "delta should not be 0" }
+ var overscrollEffect = overscrollableContent.currentOverscrollEffect
+
+ // If we're already overscrolling, continue with the current effect for a smooth finish.
+ if (overscrollEffect == null || !overscrollEffect.isInProgress) {
+ // Otherwise, determine the target content (toContent or fromContent) for the new
+ // overscroll effect based on the gesture's direction.
+ val content = animation.contentByDirection(delta)
+ overscrollEffect = overscrollableContent.applyOverscrollEffectOn(content)
+ }
+
+ // TODO(b/378470603) Remove this check once NestedDraggable is used to handle drags.
+ if (!overscrollEffect.node.node.isAttached) {
+ return drag(delta, animation)
+ }
+
+ return overscrollEffect
+ .applyToScroll(
+ delta = delta.toOffset(),
+ source = NestedScrollSource.UserInput,
+ performScroll = {
+ val preScrollAvailable = it.toFloat()
+ drag(preScrollAvailable, animation).toOffset()
+ },
+ )
+ .toFloat()
+ }
+
+ private fun <T : ContentKey> drag(delta: Float, animation: SwipeAnimation<T>): Float {
+ if (delta == 0f) return 0f
+
+ val distance = animation.distance()
+ val previousOffset = animation.dragOffset
val desiredOffset = previousOffset + delta
- val desiredProgress = swipeAnimation.computeProgress(desiredOffset)
+ val desiredProgress = animation.computeProgress(desiredOffset)
- // Note: the distance could be negative if fromContent is above or to the left of
- // toContent.
+ // Note: the distance could be negative if fromContent is above or to the left of toContent.
val newOffset =
when {
distance == DistanceUnspecified ||
- swipeAnimation.contentTransition.isWithinProgressRange(desiredProgress) ->
+ animation.contentTransition.isWithinProgressRange(desiredProgress) ->
desiredOffset
distance > 0f -> desiredOffset.fastCoerceIn(0f, distance)
else -> desiredOffset.fastCoerceIn(distance, 0f)
}
- val consumedDelta = newOffset - previousOffset
-
- swipeAnimation.dragOffset = newOffset
- val result = swipes.findUserActionResult(directionOffset = newOffset)
-
- if (result == null) {
- onCancel(canChangeContent = true)
- return 0f
- }
-
- val currentTransitionIrreversible =
- if (swipeAnimation.isUpOrLeft) {
- swipes.upOrLeftResult?.isIrreversible ?: false
- } else {
- swipes.downOrRightResult?.isIrreversible ?: false
- }
-
- val needNewTransition =
- !currentTransitionIrreversible &&
- (result.toContent(layoutState.currentScene) != swipeAnimation.toContent ||
- result.transitionKey != swipeAnimation.contentTransition.key)
-
- if (needNewTransition) {
- // Make sure the current transition will finish to the right current scene.
- swipeAnimation.currentContent = swipeAnimation.fromContent
-
- val newSwipeAnimation = draggableHandler.createSwipeAnimation(swipes, result)
- newSwipeAnimation.dragOffset = newOffset
- updateTransition(newSwipeAnimation)
- }
-
- return consumedDelta
+ animation.dragOffset = newOffset
+ return newOffset - previousOffset
}
override suspend fun onStop(velocity: Float, canChangeContent: Boolean): Float {
- return onStop(velocity, canChangeContent, swipeAnimation)
+ // To ensure that any ongoing animation completes gracefully and avoids an undefined state,
+ // we execute the actual `onStop` logic in a non-cancellable context. This prevents the
+ // coroutine from being cancelled prematurely, which could interrupt the animation.
+ // TODO(b/378470603) Remove this check once NestedDraggable is used to handle drags.
+ return withContext(NonCancellable) { onStop(velocity, canChangeContent, swipeAnimation) }
}
private suspend fun <T : ContentKey> onStop(
@@ -334,7 +352,22 @@
fromContent
}
- return swipeAnimation.animateOffset(velocity, targetContent)
+ val overscrollEffect = overscrollableContent.applyOverscrollEffectOn(targetContent)
+
+ // TODO(b/378470603) Remove this check once NestedDraggable is used to handle drags.
+ if (!overscrollEffect.node.node.isAttached) {
+ return swipeAnimation.animateOffset(velocity, targetContent)
+ }
+
+ overscrollEffect.applyToFling(
+ velocity = velocity.toVelocity(),
+ performFling = {
+ val velocityLeft = it.toFloat()
+ swipeAnimation.animateOffset(velocityLeft, targetContent).toVelocity()
+ },
+ )
+
+ return velocity
}
/**
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index 759100b..bf7e8e8 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -36,6 +36,7 @@
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
+import com.android.compose.animation.scene.effect.ContentOverscrollEffect
/**
* [SceneTransitionLayout] is a container that automatically animates its content whenever its state
@@ -283,6 +284,53 @@
@ElementDsl
interface ContentScope : BaseContentScope {
/**
+ * The overscroll effect applied to the content in the vertical direction. This can be used to
+ * customize how the content behaves when the scene is over scrolled.
+ *
+ * For example, you can use it with the `Modifier.overscroll()` modifier:
+ * ```kotlin
+ * @Composable
+ * fun ContentScope.MyScene() {
+ * Box(
+ * modifier = Modifier
+ * // Apply the effect
+ * .overscroll(verticalOverscrollEffect)
+ * ) {
+ * // ... your content ...
+ * }
+ * }
+ * ```
+ *
+ * Or you can read the `overscrollDistance` value directly, if you need some custom overscroll
+ * behavior:
+ * ```kotlin
+ * @Composable
+ * fun ContentScope.MyScene() {
+ * Box(
+ * modifier = Modifier
+ * .graphicsLayer {
+ * // Translate half of the overscroll
+ * translationY = verticalOverscrollEffect.overscrollDistance * 0.5f
+ * }
+ * ) {
+ * // ... your content ...
+ * }
+ * }
+ * ```
+ *
+ * @see horizontalOverscrollEffect
+ */
+ val verticalOverscrollEffect: ContentOverscrollEffect
+
+ /**
+ * The overscroll effect applied to the content in the horizontal direction. This can be used to
+ * customize how the content behaves when the scene is over scrolled.
+ *
+ * @see verticalOverscrollEffect
+ */
+ val horizontalOverscrollEffect: ContentOverscrollEffect
+
+ /**
* Animate some value at the content level.
*
* @param value the value of this shared value in the current content.
@@ -554,12 +602,6 @@
* bigger than 100% when the user released their finger. `
*/
open val requiresFullDistanceSwipe: Boolean,
-
- /**
- * Whether swiping back in the opposite direction past the origin point of the swipe can replace
- * the action with the action for the opposite direction.
- */
- open val isIrreversible: Boolean = false,
) {
internal abstract fun toContent(currentScene: SceneKey): ContentKey
@@ -569,7 +611,6 @@
val toScene: SceneKey,
override val transitionKey: TransitionKey? = null,
override val requiresFullDistanceSwipe: Boolean = false,
- override val isIrreversible: Boolean = false,
) : UserActionResult(transitionKey, requiresFullDistanceSwipe) {
override fun toContent(currentScene: SceneKey): ContentKey = toScene
}
@@ -579,7 +620,6 @@
val overlay: OverlayKey,
override val transitionKey: TransitionKey? = null,
override val requiresFullDistanceSwipe: Boolean = false,
- override val isIrreversible: Boolean = false,
) : UserActionResult(transitionKey, requiresFullDistanceSwipe) {
override fun toContent(currentScene: SceneKey): ContentKey = overlay
}
@@ -622,14 +662,7 @@
* the user released their finger.
*/
requiresFullDistanceSwipe: Boolean = false,
-
- /**
- * Whether swiping back in the opposite direction past the origin point of the swipe can
- * replace the action with the action for the opposite direction.
- */
- isIrreversible: Boolean = false,
- ): UserActionResult =
- ChangeScene(toScene, transitionKey, requiresFullDistanceSwipe, isIrreversible)
+ ): UserActionResult = ChangeScene(toScene, transitionKey, requiresFullDistanceSwipe)
/** A [UserActionResult] that shows [toOverlay]. */
operator fun invoke(
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 bdc1461..d7bac14 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
@@ -49,8 +49,10 @@
import com.android.compose.animation.scene.content.Overlay
import com.android.compose.animation.scene.content.Scene
import com.android.compose.animation.scene.content.state.TransitionState
+import com.android.compose.animation.scene.effect.GestureEffect
import com.android.compose.ui.util.lerp
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
/** The type for the content of movable elements. */
internal typealias MovableElementContent = @Composable (@Composable () -> Unit) -> Unit
@@ -134,6 +136,18 @@
_movableContents = it
}
+ internal var horizontalOverscrollableContent =
+ OverscrollableContent(
+ animationScope = animationScope,
+ overscrollEffect = { content(it).scope.horizontalOverscrollGestureEffect },
+ )
+
+ internal var verticalOverscrollableContent =
+ OverscrollableContent(
+ animationScope = animationScope,
+ overscrollEffect = { content(it).scope.verticalOverscrollGestureEffect },
+ )
+
/**
* The different values of a shared value keyed by a a [ValueKey] and the different elements and
* contents it is associated to.
@@ -561,3 +575,23 @@
return layout(width, height) { placeable.place(0, 0) }
}
}
+
+internal class OverscrollableContent(
+ private val animationScope: CoroutineScope,
+ private val overscrollEffect: (ContentKey) -> GestureEffect,
+) {
+ private var currentContent: ContentKey? = null
+ var currentOverscrollEffect: GestureEffect? = null
+
+ fun applyOverscrollEffectOn(contentKey: ContentKey): GestureEffect {
+ if (currentContent == contentKey) return currentOverscrollEffect!!
+
+ currentOverscrollEffect?.apply { animationScope.launch { ensureApplyToFlingIsCalled() } }
+
+ // We are wrapping the overscroll effect.
+ val overscrollEffect = overscrollEffect(contentKey)
+ currentContent = contentKey
+ currentOverscrollEffect = overscrollEffect
+ return overscrollEffect
+ }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
index ae235e5..35cdf81 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
@@ -313,6 +313,17 @@
fun isAnimatingOffset(): Boolean = offsetAnimation != null
+ /** Get the [ContentKey] ([fromContent] or [toContent]) associated to the current [direction] */
+ fun contentByDirection(direction: Float): T {
+ require(direction != 0f) { "Cannot find a content in this direction: $direction" }
+ val isDirectionToContent = (isUpOrLeft && direction < 0) || (!isUpOrLeft && direction > 0)
+ return if (isDirectionToContent) {
+ toContent
+ } else {
+ fromContent
+ }
+ }
+
/**
* Animate the offset to a [targetContent], using the [initialVelocity] and an optional [spec]
*
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
index 8c4cd8c..152f05e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
@@ -17,6 +17,7 @@
package com.android.compose.animation.scene.content
import android.annotation.SuppressLint
+import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
@@ -51,6 +52,9 @@
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.ValueKey
import com.android.compose.animation.scene.animateSharedValueAsState
+import com.android.compose.animation.scene.effect.GestureEffect
+import com.android.compose.animation.scene.effect.OffsetOverscrollEffect
+import com.android.compose.animation.scene.effect.VisualEffect
import com.android.compose.animation.scene.element
import com.android.compose.animation.scene.modifiers.noResizeDuringTransitions
import com.android.compose.animation.scene.nestedScrollToScene
@@ -109,6 +113,26 @@
override val layoutState: SceneTransitionLayoutState = layoutImpl.state
+ private val _verticalOverscrollEffect =
+ OffsetOverscrollEffect(
+ orientation = Orientation.Vertical,
+ animationScope = layoutImpl.animationScope,
+ )
+
+ private val _horizontalOverscrollEffect =
+ OffsetOverscrollEffect(
+ orientation = Orientation.Horizontal,
+ animationScope = layoutImpl.animationScope,
+ )
+
+ val verticalOverscrollGestureEffect = GestureEffect(_verticalOverscrollEffect)
+
+ val horizontalOverscrollGestureEffect = GestureEffect(_horizontalOverscrollEffect)
+
+ override val verticalOverscrollEffect = VisualEffect(_verticalOverscrollEffect)
+
+ override val horizontalOverscrollEffect = VisualEffect(_horizontalOverscrollEffect)
+
override fun Modifier.element(key: ElementKey): Modifier {
return element(layoutImpl, content, key)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/ContentOverscrollEffect.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/ContentOverscrollEffect.kt
new file mode 100644
index 0000000..2233deb
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/ContentOverscrollEffect.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene.effect
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.foundation.OverscrollEffect
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.unit.Velocity
+import com.android.compose.ui.util.SpaceVectorConverter
+import kotlin.math.abs
+import kotlin.math.sign
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * An [OverscrollEffect] that uses an [Animatable] to track and animate overscroll values along a
+ * specific [Orientation].
+ */
+interface ContentOverscrollEffect : OverscrollEffect {
+ /** The current overscroll value. */
+ val overscrollDistance: Float
+}
+
+open class BaseContentOverscrollEffect(
+ orientation: Orientation,
+ private val animationScope: CoroutineScope,
+ private val animationSpec: AnimationSpec<Float>,
+) : ContentOverscrollEffect, SpaceVectorConverter by SpaceVectorConverter(orientation) {
+
+ /** The [Animatable] that holds the current overscroll value. */
+ private val animatable = Animatable(initialValue = 0f, visibilityThreshold = 0.5f)
+
+ override val overscrollDistance: Float
+ get() = animatable.value
+
+ override val isInProgress: Boolean
+ get() = overscrollDistance != 0f
+
+ override fun applyToScroll(
+ delta: Offset,
+ source: NestedScrollSource,
+ performScroll: (Offset) -> Offset,
+ ): Offset {
+ val deltaForAxis = delta.toFloat()
+
+ // If we're currently overscrolled, and the user scrolls in the opposite direction, we need
+ // to "relax" the overscroll by consuming some of the scroll delta to bring it back towards
+ // zero.
+ val currentOffset = animatable.value
+ val sameDirection = deltaForAxis.sign == currentOffset.sign
+ val consumedByPreScroll =
+ if (abs(currentOffset) > 0.5 && !sameDirection) {
+ // The user has scrolled in the opposite direction.
+ val prevOverscrollValue = currentOffset
+ val newOverscrollValue = currentOffset + deltaForAxis
+ if (sign(prevOverscrollValue) != sign(newOverscrollValue)) {
+ // Enough to completely cancel the overscroll. We snap the overscroll value
+ // back to zero and consume the corresponding amount of the scroll delta.
+ animationScope.launch { animatable.snapTo(0f) }
+ -prevOverscrollValue
+ } else {
+ // Not enough to cancel the overscroll. We update the overscroll value
+ // accordingly and consume the entire scroll delta.
+ animationScope.launch { animatable.snapTo(newOverscrollValue) }
+ deltaForAxis
+ }
+ } else {
+ 0f
+ }
+ .toOffset()
+
+ // After handling any overscroll relaxation, we pass the remaining scroll delta to the
+ // standard scrolling logic.
+ val leftForScroll = delta - consumedByPreScroll
+ val consumedByScroll = performScroll(leftForScroll)
+ val overscrollDelta = leftForScroll - consumedByScroll
+
+ // If the user is dragging (not flinging), and there's any remaining scroll delta after the
+ // standard scrolling logic has been applied, we add it to the overscroll.
+ if (abs(overscrollDelta.toFloat()) > 0.5 && source == NestedScrollSource.UserInput) {
+ animationScope.launch { animatable.snapTo(currentOffset + overscrollDelta.toFloat()) }
+ }
+
+ return delta
+ }
+
+ override suspend fun applyToFling(
+ velocity: Velocity,
+ performFling: suspend (Velocity) -> Velocity,
+ ) {
+ // We launch a coroutine to ensure the fling animation starts after any pending [snapTo]
+ // animations have finished.
+ // This guarantees a smooth, sequential execution of animations on the overscroll value.
+ coroutineScope {
+ launch {
+ val consumed = performFling(velocity)
+ val remaining = velocity - consumed
+ animatable.animateTo(0f, animationSpec, remaining.toFloat())
+ }
+ }
+ }
+}
+
+/** An overscroll effect that ensures only a single fling animation is triggered. */
+internal class GestureEffect(private val delegate: ContentOverscrollEffect) :
+ ContentOverscrollEffect by delegate {
+ private var shouldFling = false
+
+ override fun applyToScroll(
+ delta: Offset,
+ source: NestedScrollSource,
+ performScroll: (Offset) -> Offset,
+ ): Offset {
+ shouldFling = true
+ return delegate.applyToScroll(delta, source, performScroll)
+ }
+
+ override suspend fun applyToFling(
+ velocity: Velocity,
+ performFling: suspend (Velocity) -> Velocity,
+ ) {
+ if (!shouldFling) {
+ performFling(velocity)
+ return
+ }
+ shouldFling = false
+ delegate.applyToFling(velocity, performFling)
+ }
+
+ suspend fun ensureApplyToFlingIsCalled() {
+ applyToFling(Velocity.Zero) { Velocity.Zero }
+ }
+}
+
+/**
+ * An overscroll effect that only applies visual effects and does not interfere with the actual
+ * scrolling or flinging behavior.
+ */
+internal class VisualEffect(private val delegate: ContentOverscrollEffect) :
+ ContentOverscrollEffect by delegate {
+ override fun applyToScroll(
+ delta: Offset,
+ source: NestedScrollSource,
+ performScroll: (Offset) -> Offset,
+ ): Offset {
+ return performScroll(delta)
+ }
+
+ override suspend fun applyToFling(
+ velocity: Velocity,
+ performFling: suspend (Velocity) -> Velocity,
+ ) {
+ performFling(velocity)
+ }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffect.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffect.kt
new file mode 100644
index 0000000..f459c46
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffect.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene.effect
+
+import androidx.annotation.VisibleForTesting
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.spring
+import androidx.compose.foundation.OverscrollEffect
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.node.DelegatableNode
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.ProgressConverter
+import kotlin.math.roundToInt
+import kotlinx.coroutines.CoroutineScope
+
+/** An [OverscrollEffect] that offsets the content by the overscroll value. */
+class OffsetOverscrollEffect(
+ orientation: Orientation,
+ animationScope: CoroutineScope,
+ animationSpec: AnimationSpec<Float> = DefaultAnimationSpec,
+) : BaseContentOverscrollEffect(orientation, animationScope, animationSpec) {
+ private var _node: DelegatableNode = newNode()
+ override val node: DelegatableNode
+ get() = _node
+
+ fun newNode(): DelegatableNode {
+ return object : Modifier.Node(), LayoutModifierNode {
+ override fun onDetach() {
+ super.onDetach()
+ // TODO(b/379086317) Remove this workaround: avoid to reuse the same node.
+ _node = newNode()
+ }
+
+ override fun MeasureScope.measure(
+ measurable: Measurable,
+ constraints: Constraints,
+ ): MeasureResult {
+ val placeable = measurable.measure(constraints)
+ return layout(placeable.width, placeable.height) {
+ val offsetPx = computeOffset(density = this@measure, overscrollDistance)
+ placeable.placeRelativeWithLayer(position = offsetPx.toIntOffset())
+ }
+ }
+ }
+ }
+
+ companion object {
+ private val MaxDistance = 400.dp
+
+ internal val DefaultAnimationSpec =
+ spring(
+ stiffness = Spring.StiffnessLow,
+ dampingRatio = Spring.DampingRatioLowBouncy,
+ visibilityThreshold = 0.5f,
+ )
+
+ @VisibleForTesting
+ internal fun computeOffset(density: Density, overscrollDistance: Float): Int {
+ val maxDistancePx = with(density) { MaxDistance.toPx() }
+ val progress = ProgressConverter.Default.convert(overscrollDistance / maxDistancePx)
+ return (progress * maxDistancePx).roundToInt()
+ }
+ }
+}
+
+@Composable
+fun rememberOffsetOverscrollEffect(
+ orientation: Orientation,
+ animationSpec: AnimationSpec<Float> = OffsetOverscrollEffect.DefaultAnimationSpec,
+): OffsetOverscrollEffect {
+ val animationScope = rememberCoroutineScope()
+ return remember(orientation, animationScope, animationSpec) {
+ OffsetOverscrollEffect(orientation, animationScope, animationSpec)
+ }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
index a5be4dc..98a0017 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
@@ -48,8 +48,8 @@
orientation = Orientation.Vertical,
// When swiping up, the LargeTopAppBar will shrink (to [minHeight]) and the content will
// expand. Then, you can then scroll down the content.
- canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ ->
- offsetAvailable < 0 && offsetBeforeStart == 0f && height() > minHeight()
+ canStartPreScroll = { offsetAvailable, _, _ ->
+ offsetAvailable < 0 && height() > minHeight()
},
// When swiping down, the content will scroll up until it reaches the top. Then, the
// LargeTopAppBar will expand until it reaches its [maxHeight].
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
index 3644b30..2fd1d8d8 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
@@ -18,7 +18,10 @@
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
+import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
@@ -495,4 +498,13 @@
assertThat(lastValues[SceneA]).isWithin(0.001f).of(100f)
assertThat(lastValues[SceneB]).isWithin(0.001f).of(100f)
}
+
+ @Test
+ fun interpolatedColor() {
+ val a = Color.Red
+ val b = Color.Green
+ val delta = SharedColorType.diff(b, a) // b - a
+ val interpolated = SharedColorType.addWeighted(a, delta, 0.5f) // a + (b - a) * 0.5f
+ rule.setContent { Box(Modifier.fillMaxSize().background(interpolated)) }
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index 394568d..2c8dc32 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -418,37 +418,6 @@
}
@Test
- fun onDragReversedDirection_changeToScene() = runGestureTest {
- // Drag A -> B with progress 0.6
- val dragController = onDragStarted(overSlop = -60f)
- assertTransition(
- currentScene = SceneA,
- fromScene = SceneA,
- toScene = SceneB,
- progress = 0.6f,
- )
-
- // Reverse direction such that A -> C now with 0.4
- dragController.onDragDelta(pixels = 100f)
- assertTransition(
- currentScene = SceneA,
- fromScene = SceneA,
- toScene = SceneC,
- progress = 0.4f,
- )
-
- // After the drag stopped scene C should be committed
- dragController.onDragStoppedAnimateNow(
- velocity = velocityThreshold,
- onAnimationStart = {
- assertTransition(currentScene = SceneC, fromScene = SceneA, toScene = SceneC)
- },
- expectedConsumedVelocity = velocityThreshold,
- )
- assertIdle(currentScene = SceneC)
- }
-
- @Test
fun onDragStartedWithoutActionsInBothDirections_stayIdle() = runGestureTest {
onDragStarted(
horizontalDraggableHandler,
@@ -498,31 +467,9 @@
}
@Test
- fun onDragWithActionsInBothDirections_dragToOppositeDirectionReplacesAction() = runGestureTest {
- // We are on SceneA. UP -> B, DOWN-> C.
- val dragController = onDragStarted(overSlop = up(fractionOfScreen = 0.2f))
- assertTransition(
- currentScene = SceneA,
- fromScene = SceneA,
- toScene = SceneB,
- progress = 0.2f,
- )
-
- // Reverse drag direction, it will replace the previous transition
- dragController.onDragDelta(pixels = down(fractionOfScreen = 0.5f))
- assertTransition(
- currentScene = SceneA,
- fromScene = SceneA,
- toScene = SceneC,
- progress = 0.3f,
- )
- }
-
- @Test
fun onDragWithActionsInBothDirections_dragToOppositeDirectionNotReplaceable() = runGestureTest {
// We are on SceneA. UP -> B, DOWN-> C. The up swipe is not replaceable though.
- mutableUserActionsA =
- mapOf(Swipe.Up to UserActionResult(SceneB, isIrreversible = true), Swipe.Down to SceneC)
+ mutableUserActionsA = mapOf(Swipe.Up to UserActionResult(SceneB), Swipe.Down to SceneC)
val dragController =
onDragStarted(
pointersInfo =
@@ -536,7 +483,7 @@
progress = 0.2f,
)
- // Reverse drag direction, it cannot replace the previous transition
+ // Reverse drag direction, it does not replace the previous transition.
dragController.onDragDelta(pixels = down(fractionOfScreen = 0.5f))
assertTransition(
currentScene = SceneA,
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index a301856..4410e15 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -30,6 +30,7 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.overscroll
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.material3.Text
@@ -47,6 +48,7 @@
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.approachLayout
import androidx.compose.ui.layout.layout
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertIsDisplayed
@@ -60,6 +62,7 @@
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.DpSize
@@ -72,6 +75,7 @@
import com.android.compose.animation.scene.TestScenes.SceneB
import com.android.compose.animation.scene.TestScenes.SceneC
import com.android.compose.animation.scene.content.state.TransitionState
+import com.android.compose.animation.scene.effect.OffsetOverscrollEffect
import com.android.compose.animation.scene.subjects.assertThat
import com.android.compose.test.assertSizeIsEqualTo
import com.android.compose.test.setContentAndCreateMainScope
@@ -712,7 +716,7 @@
}
@Test
- fun elementTransitionDuringOverscroll() {
+ fun elementTransitionDuringOverscrollWithOverscrollDSL() {
val layoutWidth = 200.dp
val layoutHeight = 400.dp
val overscrollTranslateY = 10.dp
@@ -765,6 +769,241 @@
assertThat(animatedFloat).isEqualTo(100f)
}
+ private fun expectedOffset(currentOffset: Dp, density: Density): Dp {
+ return with(density) {
+ OffsetOverscrollEffect.computeOffset(density, currentOffset.toPx()).toDp()
+ }
+ }
+
+ @Test
+ fun elementTransitionDuringOverscroll() {
+ val layoutWidth = 200.dp
+ val layoutHeight = 400.dp
+ lateinit var density: Density
+
+ // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
+ // detected as a drag event.
+ var touchSlop = 0f
+ val state =
+ rule.runOnUiThread {
+ MutableSceneTransitionLayoutState(
+ initialScene = SceneA,
+ transitions = transitions { overscrollDisabled(SceneB, Orientation.Vertical) },
+ )
+ }
+ rule.setContent {
+ density = LocalDensity.current
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ SceneTransitionLayout(state, Modifier.size(layoutWidth, layoutHeight)) {
+ scene(key = SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+ Spacer(Modifier.fillMaxSize())
+ }
+ scene(SceneB) {
+ Spacer(
+ Modifier.overscroll(verticalOverscrollEffect)
+ .fillMaxSize()
+ .element(TestElements.Foo)
+ )
+ }
+ }
+ }
+ assertThat(state.transitionState).isIdle()
+
+ // Swipe by half of verticalSwipeDistance.
+ rule.onRoot().performTouchInput {
+ val middleTop = Offset((layoutWidth / 2).toPx(), 0f)
+ down(middleTop)
+ // Scroll 50%.
+ val firstScrollHeight = layoutHeight.toPx() * 0.5f
+ moveBy(Offset(0f, touchSlop + firstScrollHeight), delayMillis = 1_000)
+ }
+
+ rule.onNodeWithTag(TestElements.Foo.testTag).assertTopPositionInRootIsEqualTo(0.dp)
+ val transition = assertThat(state.transitionState).isSceneTransition()
+ assertThat(transition).isNotNull()
+ assertThat(transition).hasProgress(0.5f)
+
+ rule.onRoot().performTouchInput {
+ // Scroll another 100%.
+ moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000)
+ }
+
+ // Scroll 150% (Scene B overscroll by 50%).
+ assertThat(transition).hasProgress(1f)
+
+ rule
+ .onNodeWithTag(TestElements.Foo.testTag)
+ .assertTopPositionInRootIsEqualTo(expectedOffset(layoutHeight * 0.5f, density))
+ }
+
+ @Test
+ fun elementTransitionOverscrollMultipleScenes() {
+ val layoutWidth = 200.dp
+ val layoutHeight = 400.dp
+ lateinit var density: Density
+
+ // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
+ // detected as a drag event.
+ var touchSlop = 0f
+ val state =
+ rule.runOnUiThread {
+ MutableSceneTransitionLayoutState(
+ initialScene = SceneA,
+ transitions =
+ transitions {
+ overscrollDisabled(SceneA, Orientation.Vertical)
+ overscrollDisabled(SceneB, Orientation.Vertical)
+ },
+ )
+ }
+ rule.setContent {
+ density = LocalDensity.current
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ SceneTransitionLayout(state, Modifier.size(layoutWidth, layoutHeight)) {
+ scene(key = SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+ Spacer(
+ Modifier.overscroll(verticalOverscrollEffect)
+ .fillMaxSize()
+ .element(TestElements.Foo)
+ )
+ }
+ scene(SceneB) {
+ Spacer(
+ Modifier.overscroll(verticalOverscrollEffect)
+ .fillMaxSize()
+ .element(TestElements.Bar)
+ )
+ }
+ }
+ }
+ assertThat(state.transitionState).isIdle()
+
+ // Swipe by half of verticalSwipeDistance.
+ rule.onRoot().performTouchInput {
+ val middleTop = Offset((layoutWidth / 2).toPx(), 0f)
+ down(middleTop)
+ val firstScrollHeight = layoutHeight.toPx() * 0.5f // Scroll 50%
+ moveBy(Offset(0f, touchSlop + firstScrollHeight), delayMillis = 1_000)
+ }
+
+ rule.onNodeWithTag(TestElements.Foo.testTag).assertTopPositionInRootIsEqualTo(0.dp)
+ rule.onNodeWithTag(TestElements.Bar.testTag).assertTopPositionInRootIsEqualTo(0.dp)
+ val transition = assertThat(state.transitionState).isSceneTransition()
+ assertThat(transition).isNotNull()
+ assertThat(transition).hasProgress(0.5f)
+
+ rule.onRoot().performTouchInput {
+ // Scroll another 100%.
+ moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000)
+ }
+
+ // Scroll 150% (Scene B overscroll by 50%).
+ assertThat(transition).hasProgress(1f)
+
+ rule.onNodeWithTag(TestElements.Foo.testTag).assertTopPositionInRootIsEqualTo(0.dp)
+ rule
+ .onNodeWithTag(TestElements.Bar.testTag)
+ .assertTopPositionInRootIsEqualTo(expectedOffset(layoutHeight * 0.5f, density))
+
+ rule.onRoot().performTouchInput {
+ // Scroll another -30%.
+ moveBy(Offset(0f, layoutHeight.toPx() * -0.3f), delayMillis = 1_000)
+ }
+
+ // Scroll 120% (Scene B overscroll by 20%).
+ assertThat(transition).hasProgress(1f)
+
+ rule.onNodeWithTag(TestElements.Foo.testTag).assertTopPositionInRootIsEqualTo(0.dp)
+ rule
+ .onNodeWithTag(TestElements.Bar.testTag)
+ .assertTopPositionInRootIsEqualTo(expectedOffset(layoutHeight * 0.2f, density))
+ rule.onRoot().performTouchInput {
+ // Scroll another -70%
+ moveBy(Offset(0f, layoutHeight.toPx() * -0.7f), delayMillis = 1_000)
+ }
+
+ // Scroll 50% (No overscroll).
+ assertThat(transition).hasProgress(0.5f)
+
+ rule.onNodeWithTag(TestElements.Foo.testTag).assertTopPositionInRootIsEqualTo(0.dp)
+ rule.onNodeWithTag(TestElements.Bar.testTag).assertTopPositionInRootIsEqualTo(0.dp)
+
+ rule.onRoot().performTouchInput {
+ // Scroll another -100%.
+ moveBy(Offset(0f, layoutHeight.toPx() * -1f), delayMillis = 1_000)
+ }
+
+ // Scroll -50% (Scene A overscroll by -50%).
+ assertThat(transition).hasProgress(0f)
+ rule
+ .onNodeWithTag(TestElements.Foo.testTag)
+ .assertTopPositionInRootIsEqualTo(expectedOffset(layoutHeight * -0.5f, density))
+ rule.onNodeWithTag(TestElements.Bar.testTag).assertTopPositionInRootIsEqualTo(0.dp)
+ }
+
+ @Test
+ fun elementTransitionOverscroll() {
+ val layoutWidth = 200.dp
+ val layoutHeight = 400.dp
+ lateinit var density: Density
+
+ // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
+ // detected as a drag event.
+ var touchSlop = 0f
+ val state =
+ rule.runOnUiThread {
+ MutableSceneTransitionLayoutState(
+ initialScene = SceneA,
+ transitions =
+ transitions {
+ defaultOverscrollProgressConverter = ProgressConverter.linear()
+ overscrollDisabled(SceneB, Orientation.Vertical)
+ },
+ )
+ }
+ rule.setContent {
+ density = LocalDensity.current
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ SceneTransitionLayout(state, Modifier.size(layoutWidth, layoutHeight)) {
+ scene(key = SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+ Spacer(Modifier.fillMaxSize())
+ }
+ scene(SceneB) {
+ Spacer(
+ Modifier.overscroll(verticalOverscrollEffect)
+ .element(TestElements.Foo)
+ .fillMaxSize()
+ )
+ }
+ }
+ }
+ assertThat(state.transitionState).isIdle()
+
+ // Swipe by half of verticalSwipeDistance.
+ rule.onRoot().performTouchInput {
+ val middleTop = Offset((layoutWidth / 2).toPx(), 0f)
+ down(middleTop)
+ val firstScrollHeight = layoutHeight.toPx() * 0.5f // Scroll 50%
+ moveBy(Offset(0f, touchSlop + firstScrollHeight), delayMillis = 1_000)
+ }
+
+ val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag)
+ fooElement.assertTopPositionInRootIsEqualTo(0.dp)
+ val transition = assertThat(state.transitionState).isSceneTransition()
+ assertThat(transition).isNotNull()
+ assertThat(transition).hasProgress(0.5f)
+
+ rule.onRoot().performTouchInput {
+ // Scroll another 100%.
+ moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000)
+ }
+
+ // Scroll 150% (Scene B overscroll by 50%).
+ assertThat(transition).hasProgress(1f)
+
+ fooElement.assertTopPositionInRootIsEqualTo(expectedOffset(layoutHeight * 0.5f, density))
+ }
+
@Test
fun elementTransitionDuringNestedScrollOverscroll() {
// The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index b3a3261..fe7b5b6 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -664,17 +664,11 @@
}
}
- // Swipe down for the default transition from A to B.
- rule.onRoot().performTouchInput {
- down(middle)
- moveBy(Offset(0f, touchSlop), delayMillis = 1_000)
- }
-
- assertThat(state.isTransitioning(from = SceneA, to = SceneB)).isTrue()
- assertThat(state.currentTransition?.transformationSpec?.transformationMatchers).hasSize(1)
-
// Move the pointer up to swipe to scene B using the new transition.
- rule.onRoot().performTouchInput { moveBy(Offset(0f, -1.dp.toPx()), delayMillis = 1_000) }
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy(Offset(0f, -touchSlop - 1.dp.toPx()), delayMillis = 1_000)
+ }
assertThat(state.isTransitioning(from = SceneA, to = SceneB)).isTrue()
assertThat(state.currentTransition?.transformationSpec?.transformationMatchers).hasSize(2)
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffectTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffectTest.kt
new file mode 100644
index 0000000..da8fe30
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffectTest.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.compose.animation.scene.effect
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.rememberScrollableState
+import androidx.compose.foundation.gestures.scrollable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.overscroll
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import kotlin.properties.Delegates
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class OffsetOverscrollEffectTest {
+ @get:Rule val rule = createComposeRule()
+
+ private val BOX_TAG = "box"
+
+ private data class LayoutInfo(val layoutSize: Dp, val touchSlop: Float, val density: Density) {
+ fun expectedOffset(currentOffset: Dp): Dp {
+ return with(density) {
+ OffsetOverscrollEffect.computeOffset(this, currentOffset.toPx()).toDp()
+ }
+ }
+ }
+
+ private fun setupOverscrollableBox(
+ scrollableOrientation: Orientation,
+ overscrollEffectOrientation: Orientation = scrollableOrientation,
+ ): LayoutInfo {
+ val layoutSize: Dp = 200.dp
+ var touchSlop: Float by Delegates.notNull()
+ // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
+ // detected as a drag event.
+ lateinit var density: Density
+ rule.setContent {
+ density = LocalDensity.current
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ val overscrollEffect = rememberOffsetOverscrollEffect(overscrollEffectOrientation)
+
+ Box(
+ Modifier.overscroll(overscrollEffect)
+ // A scrollable that does not consume the scroll gesture.
+ .scrollable(
+ state = rememberScrollableState { 0f },
+ orientation = scrollableOrientation,
+ overscrollEffect = overscrollEffect,
+ )
+ .size(layoutSize)
+ .testTag(BOX_TAG)
+ )
+ }
+ return LayoutInfo(layoutSize, touchSlop, density)
+ }
+
+ @Test
+ fun applyVerticalOffset_duringVerticalOverscroll() {
+ val info = setupOverscrollableBox(scrollableOrientation = Orientation.Vertical)
+
+ rule.onNodeWithTag(BOX_TAG).assertTopPositionInRootIsEqualTo(0.dp)
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy(Offset(0f, info.touchSlop + info.layoutSize.toPx()), delayMillis = 1_000)
+ }
+
+ rule
+ .onNodeWithTag(BOX_TAG)
+ .assertTopPositionInRootIsEqualTo(info.expectedOffset(info.layoutSize))
+ }
+
+ @Test
+ fun applyNoOffset_duringHorizontalOverscroll() {
+ val info =
+ setupOverscrollableBox(
+ scrollableOrientation = Orientation.Vertical,
+ overscrollEffectOrientation = Orientation.Horizontal,
+ )
+
+ rule.onNodeWithTag(BOX_TAG).assertTopPositionInRootIsEqualTo(0.dp)
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy(Offset(info.touchSlop + info.layoutSize.toPx(), 0f), delayMillis = 1_000)
+ }
+
+ rule.onNodeWithTag(BOX_TAG).assertTopPositionInRootIsEqualTo(0.dp)
+ }
+
+ @Test
+ fun backToZero_afterOverscroll() {
+ val info = setupOverscrollableBox(scrollableOrientation = Orientation.Vertical)
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy(Offset(0f, info.touchSlop + info.layoutSize.toPx()), delayMillis = 1_000)
+ }
+
+ rule
+ .onNodeWithTag(BOX_TAG)
+ .assertTopPositionInRootIsEqualTo(info.expectedOffset(info.layoutSize))
+
+ rule.onRoot().performTouchInput { up() }
+
+ rule.onNodeWithTag(BOX_TAG).assertTopPositionInRootIsEqualTo(0.dp)
+ }
+
+ @Test
+ fun offsetOverscroll_followTheTouchPointer() {
+ val info = setupOverscrollableBox(scrollableOrientation = Orientation.Vertical)
+
+ // First gesture, drag down.
+ rule.onRoot().performTouchInput {
+ down(center)
+ // A full screen scroll.
+ moveBy(Offset(0f, info.touchSlop + info.layoutSize.toPx()), delayMillis = 1_000)
+ }
+ rule
+ .onNodeWithTag(BOX_TAG)
+ .assertTopPositionInRootIsEqualTo(info.expectedOffset(info.layoutSize))
+
+ rule.onRoot().performTouchInput {
+ // Reduced by half.
+ moveBy(Offset(0f, -info.layoutSize.toPx() / 2), delayMillis = 1_000)
+ }
+ rule
+ .onNodeWithTag(BOX_TAG)
+ .assertTopPositionInRootIsEqualTo(info.expectedOffset(info.layoutSize / 2))
+
+ rule.onRoot().performTouchInput { up() }
+ // Animate back to 0.
+ rule.onNodeWithTag(BOX_TAG).assertTopPositionInRootIsEqualTo(0.dp)
+
+ // Second gesture, drag up.
+ rule.onRoot().performTouchInput {
+ down(center)
+ // A full screen scroll.
+ moveBy(Offset(0f, -info.touchSlop - info.layoutSize.toPx()), delayMillis = 1_000)
+ }
+ rule
+ .onNodeWithTag(BOX_TAG)
+ .assertTopPositionInRootIsEqualTo(info.expectedOffset(-info.layoutSize))
+ }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
index e27f9b5..c8fb2cb 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
@@ -71,41 +71,6 @@
}
@Test
- fun onScrollUpAfterContentScrolled_ignoreUpEvent() {
- val scrollConnection = buildScrollConnection(heightRange = 0f..2f)
- height = 1f
-
- // scroll down consumed by a child
- scrollConnection.scroll(available = Offset(0f, 1f), consumedByScroll = Offset(0f, 1f))
-
- val offsetConsumed =
- scrollConnection.onPreScroll(available = Offset(x = 0f, y = -1f), source = scrollSource)
-
- // It should ignore all onPreScroll events
- assertThat(offsetConsumed).isEqualTo(Offset.Zero)
- assertThat(height).isEqualTo(1f)
- }
-
- @Test
- fun onScrollUpAfterContentReturnedToZero_consumeHeight() {
- val scrollConnection = buildScrollConnection(heightRange = 0f..2f)
- height = 1f
-
- // scroll down consumed by a child
- scrollConnection.scroll(available = Offset(0f, 1f), consumedByScroll = Offset(0f, 1f))
-
- // scroll up consumed by a child, the child is in its original position
- scrollConnection.scroll(available = Offset(0f, -1f), consumedByScroll = Offset(0f, -1f))
-
- val offsetConsumed =
- scrollConnection.onPreScroll(available = Offset(x = 0f, y = -1f), source = scrollSource)
-
- // It should ignore all onPreScroll events
- assertThat(offsetConsumed).isEqualTo(Offset(0f, -1f))
- assertThat(height).isEqualTo(0f)
- }
-
- @Test
fun onScrollUp_consumeDownToMin() {
val scrollConnection = buildScrollConnection(heightRange = 0f..2f)
height = 0f
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 300a3e2..ad9eba8 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -106,10 +106,8 @@
largeClock.animations = LargeClockAnimations(largeClock.view, dozeFraction, foldFraction)
smallClock.animations = DefaultClockAnimations(smallClock.view, dozeFraction, foldFraction)
- val theme = ThemeConfig(isDarkTheme, settings?.seedColor)
- largeClock.events.onThemeChanged(theme)
- smallClock.events.onThemeChanged(theme)
-
+ largeClock.events.onThemeChanged(largeClock.theme.copy(isDarkTheme = isDarkTheme))
+ smallClock.events.onThemeChanged(smallClock.theme.copy(isDarkTheme = isDarkTheme))
events.onTimeZoneChanged(TimeZone.getDefault())
smallClock.events.onTimeTick()
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
index c7a3f63..7f01fd7 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
@@ -24,7 +24,6 @@
import com.android.systemui.plugins.clocks.ClockEvents
import com.android.systemui.plugins.clocks.ClockFontAxis
import com.android.systemui.plugins.clocks.ClockFontAxisSetting
-import com.android.systemui.plugins.clocks.ThemeConfig
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
import com.android.systemui.shared.clocks.view.FlexClockView
@@ -107,18 +106,16 @@
}
override fun initialize(isDarkTheme: Boolean, dozeFraction: Float, foldFraction: Float) {
- val theme = ThemeConfig(isDarkTheme, clockCtx.settings.seedColor)
events.onFontAxesChanged(clockCtx.settings.axes)
-
smallClock.run {
- events.onThemeChanged(theme)
+ events.onThemeChanged(theme.copy(isDarkTheme = isDarkTheme))
animations.doze(dozeFraction)
animations.fold(foldFraction)
events.onTimeTick()
}
largeClock.run {
- events.onThemeChanged(theme)
+ events.onThemeChanged(theme.copy(isDarkTheme = isDarkTheme))
animations.doze(dozeFraction)
animations.fold(foldFraction)
events.onTimeTick()
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
index a8890e6..21d41ae 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
@@ -131,6 +131,7 @@
}
override fun onThemeChanged(theme: ThemeConfig) {
+ this@FlexClockFaceController.theme = theme
layerController.faceEvents.onThemeChanged(theme)
}
diff --git a/packages/SystemUI/lint-baseline.xml b/packages/SystemUI/lint-baseline.xml
index 00b0c44..43131b1 100644
--- a/packages/SystemUI/lint-baseline.xml
+++ b/packages/SystemUI/lint-baseline.xml
@@ -33644,7 +33644,7 @@
<issue
id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window
should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only
@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so
might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).
If the usage of Resources is not related to display specific configuration or UI, then there is
technically no need to use the annotation, and you can annotate the class with
@SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Resources is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
errorLine1=" @Main resources: Resources,"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -33655,7 +33655,7 @@
<issue
id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window
should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only
@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so
might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).
If the usage of Context is not related to display specific configuration or UI, then there is
technically no need to use the annotation, and you can annotate the class with
@SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Context is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
errorLine1="constructor(context: Context, val shadeViewController: ShadeViewController) {"
errorLine2=" ~~~~~~~~~~~~~~~~">
<location
@@ -33699,7 +33699,7 @@
<issue
id="ShadeDisplayAwareContextChecker"
- message="UI elements of the shade window
should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only
@ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so
might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme).
If the usage of Resources is not related to display specific configuration or UI, then there is
technically no need to use the annotation, and you can annotate the class with
@SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Resources is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
errorLine1=" @Main private val resources: Resources,"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -33763,5 +33763,201 @@
column="5"/>
</issue>
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Context is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ errorLine1=" public AuthController(Context context,"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java"
+ line="716"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated WindowManager, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of WindowManager is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ errorLine1=" @NonNull WindowManager windowManager,"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java"
+ line="721"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Context is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ errorLine1=" private val sysuiContext: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt"
+ line="72"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated ConfigurationController, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of ConfigurationController is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ errorLine1=" private val configurationController: ConfigurationController,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt"
+ line="74"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Context is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ errorLine1=" Context context,"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationBroadcastReceiver.java"
+ line="46"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Resources is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ errorLine1=" @Main Resources resources,"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationDialogFactory.java"
+ line="52"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Context is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ errorLine1=" public BiometricNotificationService(@NonNull Context context,"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java"
+ line="148"
+ column="58"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Context is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ errorLine1=" c: Context,"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt"
+ line="61"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Resources is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt"
+ line="39"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Resources is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt"
+ line="37"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Resources is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt"
+ line="42"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Context is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ errorLine1=" @NonNull final Context context,"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java"
+ line="186"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Context is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ errorLine1="class IconBuilder @Inject constructor(private val context: Context) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt"
+ line="27"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated WindowManager, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of WindowManager is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ errorLine1=" private val windowManager: WindowManager,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt"
+ line="40"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Resources is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ errorLine1=" @Main resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt"
+ line="53"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated Resources, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Resources is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ errorLine1=" @Main private val resources: Resources,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddable.kt"
+ line="51"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated Context, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of Context is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ errorLine1=" private val context: Context,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt"
+ line="51"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="ShadeDisplayAwareContextChecker"
+ message="UI elements of the shade window should use ShadeDisplayAware-annotated WindowManager, as the shade might move between windows, and only @ShadeDisplayAware resources are updated with the new configuration correctly. Failures to do so might result in wrong dimensions for shade window classes (e.g. using the wrong density or theme). If the usage of WindowManager is not related to display specific configuration or UI, then there is technically no need to use the annotation, and you can annotate the class with @SuppressLint("ShadeDisplayAwareContextChecker")/@Suppress("ShadeDisplayAwareContextChecker")"
+ errorLine1=" windowManager: WindowManager,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt"
+ line="53"
+ column="5"/>
</issues>
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ExpandHelperTest.java
index 1b07241..7fb879c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ExpandHelperTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ExpandHelperTest.java
@@ -31,7 +31,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.animation.AnimatorTestRule;
-import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.FakeFeatureFlagsClassic;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -49,7 +49,7 @@
@Rule
public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(this);
- private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
+ private final FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic();
private ExpandableNotificationRow mRow;
private ExpandHelper mExpandHelper;
private ExpandHelper.Callback mCallback;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index 9d471f4..ad12c61 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -139,13 +139,11 @@
private ActivityInfo mActivityInfo;
@Mock
private Drawable mDrawable;
- @Mock
- private HearingDevicesPresetsController mPresetsController;
+
private SystemUIDialog mDialog;
private SystemUIDialog.Factory mDialogFactory;
private HearingDevicesDialogDelegate mDialogDelegate;
private TestableLooper mTestableLooper;
- private final List<CachedBluetoothDevice> mDevices = new ArrayList<>();
@Before
public void setUp() {
@@ -155,7 +153,7 @@
when(mProfileManager.getHapClientProfile()).thenReturn(mHapClientProfile);
when(mLocalBluetoothAdapter.isEnabled()).thenReturn(true);
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
- when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(mDevices);
+ when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(List.of(mCachedDevice));
when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
when(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState);
when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
@@ -163,6 +161,7 @@
when(mCachedDevice.getDevice()).thenReturn(mDevice);
when(mCachedDevice.getAddress()).thenReturn(DEVICE_ADDRESS);
when(mCachedDevice.getName()).thenReturn(DEVICE_NAME);
+ when(mCachedDevice.getProfiles()).thenReturn(List.of(mHapClientProfile));
when(mCachedDevice.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(true);
when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
when(mCachedDevice.isConnectedHapClientDevice()).thenReturn(true);
@@ -170,12 +169,11 @@
when(mHearingDeviceItem.getCachedBluetoothDevice()).thenReturn(mCachedDevice);
mContext.setMockPackageManager(mPackageManager);
- mDevices.add(mCachedDevice);
}
@Test
public void clickPairNewDeviceButton_intentActionMatch() {
- setUpPairNewDeviceDialog();
+ setUpDeviceDialogWithPairNewDeviceButton();
mDialog.show();
getPairNewDeviceButton(mDialog).performClick();
@@ -191,7 +189,7 @@
@Test
public void onDeviceItemGearClicked_intentActionMatch() {
- setUpDeviceListDialog();
+ setUpDeviceDialogWithoutPairNewDeviceButton();
mDialogDelegate.onDeviceItemGearClicked(mHearingDeviceItem, new View(mContext));
@@ -206,7 +204,7 @@
@Test
public void onDeviceItemOnClicked_connectedDevice_disconnect() {
- setUpDeviceListDialog();
+ setUpDeviceDialogWithoutPairNewDeviceButton();
when(mHearingDeviceItem.getType()).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE);
mDialogDelegate.onDeviceItemClicked(mHearingDeviceItem, new View(mContext));
@@ -222,7 +220,7 @@
mContext.getOrCreateTestableResources().addOverride(
R.array.config_quickSettingsHearingDevicesRelatedToolName, new String[]{});
- setUpPairNewDeviceDialog();
+ setUpDeviceDialogWithoutPairNewDeviceButton();
mDialog.show();
assertToolsUi(0);
@@ -237,7 +235,7 @@
mContext.getOrCreateTestableResources().addOverride(
R.array.config_quickSettingsHearingDevicesRelatedToolName, new String[]{});
- setUpPairNewDeviceDialog();
+ setUpDeviceDialogWithoutPairNewDeviceButton();
mDialog.show();
assertToolsUi(1);
@@ -247,9 +245,8 @@
@EnableFlags(Flags.FLAG_HEARING_DEVICES_DIALOG_RELATED_TOOLS)
public void showDialog_hasLiveCaption_oneRelatedToolInConfig_showTwoRelatedTools()
throws PackageManager.NameNotFoundException {
- when(mPackageManager.queryIntentActivities(
- eq(LIVE_CAPTION_INTENT), anyInt())).thenReturn(
- List.of(new ResolveInfo()));
+ when(mPackageManager.queryIntentActivities(eq(LIVE_CAPTION_INTENT), anyInt()))
+ .thenReturn(List.of(new ResolveInfo()));
mContext.getOrCreateTestableResources().addOverride(
R.array.config_quickSettingsHearingDevicesRelatedToolName,
new String[]{TEST_PKG + "/" + TEST_CLS});
@@ -260,18 +257,18 @@
when(mActivityInfo.getComponentName()).thenReturn(TEST_COMPONENT);
when(mDrawable.mutate()).thenReturn(mDrawable);
- setUpPairNewDeviceDialog();
+ setUpDeviceDialogWithoutPairNewDeviceButton();
mDialog.show();
assertToolsUi(2);
}
@Test
- public void showDialog_noPreset_presetGone() {
- when(mPresetsController.getAllPresetInfo()).thenReturn(new ArrayList<>());
- when(mPresetsController.getActivePresetIndex()).thenReturn(PRESET_INDEX_UNAVAILABLE);
+ public void showDialog_noPreset_presetLayoutGone() {
+ when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(new ArrayList<>());
+ when(mHapClientProfile.getActivePresetIndex(mDevice)).thenReturn(PRESET_INDEX_UNAVAILABLE);
- setUpDeviceListDialog();
+ setUpDeviceDialogWithoutPairNewDeviceButton();
mDialog.show();
ViewGroup presetLayout = getPresetLayout(mDialog);
@@ -281,11 +278,12 @@
@Test
public void showDialog_presetExist_presetSelected() {
BluetoothHapPresetInfo info = getTestPresetInfo();
- when(mPresetsController.getAllPresetInfo()).thenReturn(List.of(info));
- when(mPresetsController.getActivePresetIndex()).thenReturn(TEST_PRESET_INDEX);
+ when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(List.of(info));
+ when(mHapClientProfile.getActivePresetIndex(mDevice)).thenReturn(TEST_PRESET_INDEX);
- setUpDeviceListDialog();
+ setUpDeviceDialogWithoutPairNewDeviceButton();
mDialog.show();
+ mTestableLooper.processAllMessages();
ViewGroup presetLayout = getPresetLayout(mDialog);
assertThat(presetLayout.getVisibility()).isEqualTo(View.VISIBLE);
@@ -295,48 +293,32 @@
@Test
public void onActiveDeviceChanged_presetExist_presetSelected() {
- setUpDeviceListDialog();
+ setUpDeviceDialogWithoutPairNewDeviceButton();
mDialog.show();
BluetoothHapPresetInfo info = getTestPresetInfo();
- when(mPresetsController.getAllPresetInfo()).thenReturn(List.of(info));
- when(mPresetsController.getActivePresetIndex()).thenReturn(TEST_PRESET_INDEX);
+ when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(List.of(info));
+ when(mHapClientProfile.getActivePresetIndex(mDevice)).thenReturn(TEST_PRESET_INDEX);
+
+ Spinner spinner = getPresetSpinner(mDialog);
+ assertThat(spinner.getSelectedItemPosition()).isEqualTo(-1);
mDialogDelegate.onActiveDeviceChanged(mCachedDevice, BluetoothProfile.LE_AUDIO);
mTestableLooper.processAllMessages();
ViewGroup presetLayout = getPresetLayout(mDialog);
assertThat(presetLayout.getVisibility()).isEqualTo(View.VISIBLE);
- Spinner spinner = getPresetSpinner(mDialog);
assertThat(spinner.getSelectedItemPosition()).isEqualTo(0);
}
-
-
- private void setUpPairNewDeviceDialog() {
- mDialogFactory = new SystemUIDialog.Factory(
- mContext,
- mSystemUIDialogManager,
- mSysUiState,
- getFakeBroadcastDispatcher(),
- mDialogTransitionAnimator
- );
- mDialogDelegate = new HearingDevicesDialogDelegate(
- mContext,
- true,
- TEST_LAUNCH_SOURCE_ID,
- mDialogFactory,
- mActivityStarter,
- mDialogTransitionAnimator,
- mLocalBluetoothManager,
- new Handler(mTestableLooper.getLooper()),
- mAudioManager,
- mUiEventLogger
- );
-
- mDialog = mDialogDelegate.createDialog();
+ private void setUpDeviceDialogWithPairNewDeviceButton() {
+ setUpDeviceDialog(/* showPairNewDevice= */ true);
}
- private void setUpDeviceListDialog() {
+ private void setUpDeviceDialogWithoutPairNewDeviceButton() {
+ setUpDeviceDialog(/* showPairNewDevice= */ false);
+ }
+
+ private void setUpDeviceDialog(boolean showPairNewDevice) {
mDialogFactory = new SystemUIDialog.Factory(
mContext,
mSystemUIDialogManager,
@@ -345,8 +327,7 @@
mDialogTransitionAnimator
);
mDialogDelegate = new HearingDevicesDialogDelegate(
- mContext,
- false,
+ showPairNewDevice,
TEST_LAUNCH_SOURCE_ID,
mDialogFactory,
mActivityStarter,
@@ -356,15 +337,14 @@
mAudioManager,
mUiEventLogger
);
-
mDialog = mDialogDelegate.createDialog();
- mDialogDelegate.setHearingDevicesPresetsController(mPresetsController);
}
private BluetoothHapPresetInfo getTestPresetInfo() {
BluetoothHapPresetInfo info = mock(BluetoothHapPresetInfo.class);
when(info.getName()).thenReturn(TEST_PRESET_NAME);
when(info.getIndex()).thenReturn(TEST_PRESET_INDEX);
+ when(info.isAvailable()).thenReturn(true);
return info;
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java
index 2ac5d10..c9779c9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java
@@ -21,10 +21,10 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyList;
-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 static org.mockito.kotlin.VerificationKt.never;
import static java.util.Collections.emptyList;
@@ -39,7 +39,6 @@
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HapClientProfile;
-import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.systemui.SysuiTestCase;
@@ -53,6 +52,7 @@
import org.mockito.junit.MockitoRule;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.Executor;
/** Tests for {@link HearingDevicesPresetsController}. */
@@ -62,6 +62,7 @@
public class HearingDevicesPresetsControllerTest extends SysuiTestCase {
private static final int TEST_PRESET_INDEX = 1;
+ private static final int TEST_UPDATED_PRESET_INDEX = 2;
private static final String TEST_PRESET_NAME = "test_preset";
private static final int TEST_HAP_GROUP_ID = 1;
private static final int TEST_REASON = 1024;
@@ -74,14 +75,13 @@
@Mock
private HapClientProfile mHapClientProfile;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
+ private CachedBluetoothDevice mCachedDevice;
@Mock
- private CachedBluetoothDevice mSubCachedBluetoothDevice;
+ private CachedBluetoothDevice mCachedMemberDevice;
@Mock
- private BluetoothDevice mBluetoothDevice;
+ private BluetoothDevice mDevice;
@Mock
- private BluetoothDevice mSubBluetoothDevice;
-
+ private BluetoothDevice mMemberDevice;
@Mock
private HearingDevicesPresetsController.PresetCallback mCallback;
@@ -91,15 +91,19 @@
public void setUp() {
when(mProfileManager.getHapClientProfile()).thenReturn(mHapClientProfile);
when(mHapClientProfile.isProfileReady()).thenReturn(true);
- when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
- when(mCachedBluetoothDevice.getSubDevice()).thenReturn(mSubCachedBluetoothDevice);
- when(mSubCachedBluetoothDevice.getDevice()).thenReturn(mSubBluetoothDevice);
+ when(mCachedDevice.getDevice()).thenReturn(mDevice);
+ when(mCachedDevice.getProfiles()).thenReturn(List.of(mHapClientProfile));
+ when(mCachedDevice.getMemberDevice()).thenReturn(Set.of(mCachedMemberDevice));
+ when(mCachedMemberDevice.getDevice()).thenReturn(mMemberDevice);
mController = new HearingDevicesPresetsController(mProfileManager, mCallback);
+ mController.setDevice(mCachedDevice);
}
@Test
public void onServiceConnected_callExpectedCallback() {
+ preparePresetInfo(/* isValid= */ true);
+
mController.onServiceConnected();
verify(mHapClientProfile).registerCallback(any(Executor.class),
@@ -108,115 +112,129 @@
}
@Test
- public void getAllPresetInfo_setInvalidHearingDevice_getEmpty() {
- when(mCachedBluetoothDevice.getProfiles()).thenReturn(emptyList());
- mController.setHearingDeviceIfSupportHap(mCachedBluetoothDevice);
- BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true);
- when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
- List.of(hapPresetInfo));
+ public void setDevice_nonHapDevice_getEmptyListAndInvalidActiveIndex() {
+ when(mCachedDevice.getProfiles()).thenReturn(emptyList());
+ preparePresetInfo(/* isValid= */ true);
+
+ mController.setDevice(mCachedDevice);
+
+ assertThat(mController.getAllPresetInfo()).isEmpty();
+ assertThat(mController.getActivePresetIndex()).isEqualTo(
+ BluetoothHapClient.PRESET_INDEX_UNAVAILABLE);
+ }
+
+ @Test
+ public void refreshPresetInfo_containsOnlyNotAvailablePresetInfo_getEmptyList() {
+ preparePresetInfo(/* isValid= */ false);
+
+ mController.refreshPresetInfo();
assertThat(mController.getAllPresetInfo()).isEmpty();
}
@Test
- public void getAllPresetInfo_containsNotAvailablePresetInfo_getEmpty() {
- setValidHearingDeviceSupportHap();
- BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(false);
- when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
- List.of(hapPresetInfo));
+ public void refreshPresetInfo_containsOnePresetInfo_getOnePresetInfo() {
+ List<BluetoothHapPresetInfo> infos = preparePresetInfo(/* isValid= */ true);
- assertThat(mController.getAllPresetInfo()).isEmpty();
+ mController.refreshPresetInfo();
+
+ List<BluetoothHapPresetInfo> presetInfos = mController.getAllPresetInfo();
+ assertThat(presetInfos.size()).isEqualTo(1);
+ assertThat(presetInfos).contains(infos.getFirst());
}
@Test
- public void getAllPresetInfo_containsOnePresetInfo_getOnePresetInfo() {
- setValidHearingDeviceSupportHap();
- BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true);
- when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
- List.of(hapPresetInfo));
+ public void refreshPresetInfo_getExpectedIndex() {
+ preparePresetInfo(/* isValid= */ true);
- assertThat(mController.getAllPresetInfo()).contains(hapPresetInfo);
- }
-
- @Test
- public void getActivePresetIndex_getExpectedIndex() {
- setValidHearingDeviceSupportHap();
- when(mHapClientProfile.getActivePresetIndex(mBluetoothDevice)).thenReturn(
- TEST_PRESET_INDEX);
+ mController.refreshPresetInfo();
assertThat(mController.getActivePresetIndex()).isEqualTo(TEST_PRESET_INDEX);
}
@Test
- public void onPresetSelected_presetIndex_callOnPresetInfoUpdatedWithExpectedPresetIndex() {
- setValidHearingDeviceSupportHap();
- BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true);
- when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
- List.of(hapPresetInfo));
- when(mHapClientProfile.getActivePresetIndex(mBluetoothDevice)).thenReturn(
- TEST_PRESET_INDEX);
+ public void refreshPresetInfo_callbackIsCalledWhenNeeded() {
+ List<BluetoothHapPresetInfo> infos = preparePresetInfo(/* isValid= */ true);
- mController.onPresetSelected(mBluetoothDevice, TEST_PRESET_INDEX, TEST_REASON);
+ mController.refreshPresetInfo();
- verify(mCallback).onPresetInfoUpdated(eq(List.of(hapPresetInfo)), eq(TEST_PRESET_INDEX));
+ verify(mCallback).onPresetInfoUpdated(infos, TEST_PRESET_INDEX);
+
+ Mockito.reset(mCallback);
+ mController.refreshPresetInfo();
+
+ verify(mCallback, never()).onPresetInfoUpdated(anyList(), anyInt());
+
+ Mockito.reset(mCallback);
+ when(mHapClientProfile.getActivePresetIndex(mDevice)).thenReturn(TEST_UPDATED_PRESET_INDEX);
+ mController.refreshPresetInfo();
+
+ verify(mCallback).onPresetInfoUpdated(infos, TEST_UPDATED_PRESET_INDEX);
}
@Test
- public void onPresetInfoChanged_presetIndex_callOnPresetInfoUpdatedWithExpectedPresetIndex() {
- setValidHearingDeviceSupportHap();
- BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true);
- when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
- List.of(hapPresetInfo));
- when(mHapClientProfile.getActivePresetIndex(mBluetoothDevice)).thenReturn(
- TEST_PRESET_INDEX);
+ public void onPresetSelected_callOnPresetInfoUpdatedWithExpectedPresetIndex() {
+ List<BluetoothHapPresetInfo> infos = preparePresetInfo(/* isValid= */ true);
- mController.onPresetInfoChanged(mBluetoothDevice, List.of(hapPresetInfo), TEST_REASON);
+ mController.onPresetSelected(mDevice, TEST_PRESET_INDEX, TEST_REASON);
- verify(mCallback).onPresetInfoUpdated(List.of(hapPresetInfo), TEST_PRESET_INDEX);
+ verify(mCallback).onPresetInfoUpdated(infos, TEST_PRESET_INDEX);
+ }
+
+ @Test
+ public void onPresetInfoChanged_callOnPresetInfoUpdatedWithExpectedPresetIndex() {
+ List<BluetoothHapPresetInfo> infos = preparePresetInfo(/* isValid= */ true);
+
+ mController.onPresetInfoChanged(mDevice, infos, TEST_REASON);
+
+ verify(mCallback).onPresetInfoUpdated(infos, TEST_PRESET_INDEX);
}
@Test
public void onPresetSelectionFailed_callOnPresetCommandFailed() {
- setValidHearingDeviceSupportHap();
-
- mController.onPresetSelectionFailed(mBluetoothDevice, TEST_REASON);
+ mController.onPresetSelectionFailed(mDevice, TEST_REASON);
verify(mCallback).onPresetCommandFailed(TEST_REASON);
}
@Test
public void onSetPresetNameFailed_callOnPresetCommandFailed() {
- setValidHearingDeviceSupportHap();
-
- mController.onSetPresetNameFailed(mBluetoothDevice, TEST_REASON);
+ mController.onSetPresetNameFailed(mDevice, TEST_REASON);
verify(mCallback).onPresetCommandFailed(TEST_REASON);
}
@Test
- public void onPresetSelectionForGroupFailed_callSelectPresetIndividual() {
- setValidHearingDeviceSupportHap();
+ public void onPresetSelectionForGroupFailed_callSelectPresetIndependently() {
mController.selectPreset(TEST_PRESET_INDEX);
Mockito.reset(mHapClientProfile);
- when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(TEST_HAP_GROUP_ID);
+ when(mHapClientProfile.getHapGroup(mDevice)).thenReturn(TEST_HAP_GROUP_ID);
mController.onPresetSelectionForGroupFailed(TEST_HAP_GROUP_ID, TEST_REASON);
-
- verify(mHapClientProfile).selectPreset(mBluetoothDevice, TEST_PRESET_INDEX);
- verify(mHapClientProfile).selectPreset(mSubBluetoothDevice, TEST_PRESET_INDEX);
+ verify(mHapClientProfile).selectPreset(mDevice, TEST_PRESET_INDEX);
+ verify(mHapClientProfile).selectPreset(mMemberDevice, TEST_PRESET_INDEX);
}
@Test
public void onSetPresetNameForGroupFailed_callOnPresetCommandFailed() {
- setValidHearingDeviceSupportHap();
-
mController.onSetPresetNameForGroupFailed(TEST_HAP_GROUP_ID, TEST_REASON);
verify(mCallback).onPresetCommandFailed(TEST_REASON);
}
@Test
+ public void registerHapCallback_profileNotReady_addServiceListener() {
+ when(mHapClientProfile.isProfileReady()).thenReturn(false);
+
+ mController.registerHapCallback();
+
+ verify(mProfileManager).addServiceListener(mController);
+ verify(mHapClientProfile, never()).registerCallback(any(Executor.class),
+ any(BluetoothHapClient.Callback.class));
+ }
+
+ @Test
public void registerHapCallback_callHapRegisterCallback() {
mController.registerHapCallback();
@@ -233,9 +251,8 @@
@Test
public void selectPreset_supportSynchronized_validGroupId_callSelectPresetForGroup() {
- setValidHearingDeviceSupportHap();
- when(mHapClientProfile.supportsSynchronizedPresets(mBluetoothDevice)).thenReturn(true);
- when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(TEST_HAP_GROUP_ID);
+ when(mHapClientProfile.supportsSynchronizedPresets(mDevice)).thenReturn(true);
+ when(mHapClientProfile.getHapGroup(mDevice)).thenReturn(TEST_HAP_GROUP_ID);
mController.selectPreset(TEST_PRESET_INDEX);
@@ -243,28 +260,34 @@
}
@Test
- public void selectPreset_supportSynchronized_invalidGroupId_callSelectPresetIndividual() {
- setValidHearingDeviceSupportHap();
- when(mHapClientProfile.supportsSynchronizedPresets(mBluetoothDevice)).thenReturn(true);
- when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(
+ public void selectPreset_supportSynchronized_invalidGroupId_callSelectPresetIndependently() {
+ when(mHapClientProfile.supportsSynchronizedPresets(mDevice)).thenReturn(true);
+ when(mHapClientProfile.getHapGroup(mDevice)).thenReturn(
BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
mController.selectPreset(TEST_PRESET_INDEX);
- verify(mHapClientProfile).selectPreset(mBluetoothDevice, TEST_PRESET_INDEX);
- verify(mHapClientProfile).selectPreset(mSubBluetoothDevice, TEST_PRESET_INDEX);
+ verify(mHapClientProfile).selectPreset(mDevice, TEST_PRESET_INDEX);
+ verify(mHapClientProfile).selectPreset(mMemberDevice, TEST_PRESET_INDEX);
}
@Test
- public void selectPreset_notSupportSynchronized_validGroupId_callSelectPresetIndividual() {
- setValidHearingDeviceSupportHap();
- when(mHapClientProfile.supportsSynchronizedPresets(mBluetoothDevice)).thenReturn(false);
- when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(TEST_HAP_GROUP_ID);
+ public void selectPreset_notSupportSynchronized_validGroupId_callSelectPresetIndependently() {
+ when(mHapClientProfile.supportsSynchronizedPresets(mDevice)).thenReturn(false);
+ when(mHapClientProfile.getHapGroup(mDevice)).thenReturn(TEST_HAP_GROUP_ID);
mController.selectPreset(TEST_PRESET_INDEX);
- verify(mHapClientProfile).selectPreset(mBluetoothDevice, TEST_PRESET_INDEX);
- verify(mHapClientProfile).selectPreset(mSubBluetoothDevice, TEST_PRESET_INDEX);
+ verify(mHapClientProfile).selectPreset(mDevice, TEST_PRESET_INDEX);
+ verify(mHapClientProfile).selectPreset(mMemberDevice, TEST_PRESET_INDEX);
+ }
+
+ private List<BluetoothHapPresetInfo> preparePresetInfo(boolean isValid) {
+ BluetoothHapPresetInfo info = getHapPresetInfo(isValid);
+ List<BluetoothHapPresetInfo> infos = List.of(info);
+ when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(infos);
+ when(mHapClientProfile.getActivePresetIndex(mDevice)).thenReturn(TEST_PRESET_INDEX);
+ return infos;
}
private BluetoothHapPresetInfo getHapPresetInfo(boolean available) {
@@ -274,12 +297,4 @@
when(info.isAvailable()).thenReturn(available);
return info;
}
-
- private void setValidHearingDeviceSupportHap() {
- LocalBluetoothProfile hapClientProfile = mock(HapClientProfile.class);
- List<LocalBluetoothProfile> profiles = List.of(hapClientProfile);
- when(mCachedBluetoothDevice.getProfiles()).thenReturn(profiles);
-
- mController.setHearingDeviceIfSupportHap(mCachedBluetoothDevice);
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 2817f55..acc97a9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -43,6 +43,7 @@
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
+import android.app.KeyguardManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -182,6 +183,8 @@
@Captor
private ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mFaceAuthenticatorsRegisteredCaptor;
@Captor
+ private ArgumentCaptor<KeyguardManager.KeyguardLockedStateListener> mKeyguardLockedStateCaptor;
+ @Captor
private ArgumentCaptor<BiometricStateListener> mBiometricStateCaptor;
@Captor
private ArgumentCaptor<Integer> mModalityCaptor;
@@ -192,6 +195,8 @@
@Mock
private VibratorHelper mVibratorHelper;
@Mock
+ private KeyguardManager mKeyguardManager;
+ @Mock
private MSDLPlayer mMSDLPlayer;
private TestableContext mContextSpy;
@@ -272,6 +277,9 @@
mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(fpProps);
mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(faceProps);
+ verify(mKeyguardManager).addKeyguardLockedStateListener(any(),
+ mKeyguardLockedStateCaptor.capture());
+
// Ensures that the operations posted on the handler get executed.
waitForIdleSync();
}
@@ -977,6 +985,18 @@
}
@Test
+ public void testCloseDialog_whenDeviceLocks() throws Exception {
+ showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */);
+
+ mKeyguardLockedStateCaptor.getValue().onKeyguardLockedStateChanged(
+ true /* isKeyguardLocked */);
+
+ verify(mReceiver).onDialogDismissed(
+ eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL),
+ eq(null) /* credentialAttestation */);
+ }
+
+ @Test
public void testShowDialog_whenOwnerNotInForeground() {
PromptInfo promptInfo = createTestPromptInfo();
promptInfo.setAllowBackgroundAuthentication(false);
@@ -1193,7 +1213,7 @@
mWakefulnessLifecycle, mUserManager, mLockPatternUtils, () -> mUdfpsLogger,
() -> mLogContextInteractor, () -> mPromptSelectionInteractor,
() -> mCredentialViewModel, () -> mPromptViewModel, mInteractionJankMonitor,
- mHandler, mBackgroundExecutor, mUdfpsUtils, mVibratorHelper,
+ mHandler, mBackgroundExecutor, mUdfpsUtils, mVibratorHelper, mKeyguardManager,
mLazyViewCapture, mMSDLPlayer);
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
index 4e64c50..297aee5c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
@@ -17,6 +17,8 @@
package com.android.systemui.biometrics.domain.interactor
import android.graphics.Rect
+import android.hardware.fingerprint.FingerprintManager
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.view.MotionEvent
import android.view.Surface
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -24,6 +26,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.authController
+import com.android.systemui.biometrics.fingerprintManager
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
@@ -39,6 +42,8 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.verify
@@ -57,6 +62,8 @@
private val testScope: TestScope = kosmos.testScope
private val authController: AuthController = kosmos.authController
+ private val fingerprintManager: FingerprintManager = kosmos.fingerprintManager
+ @Mock private lateinit var fingerprintSensorProperties: FingerprintSensorPropertiesInternal
@Captor private lateinit var authControllerCallback: ArgumentCaptor<AuthController.Callback>
@Mock private lateinit var udfpsOverlayParams: UdfpsOverlayParams
@@ -122,6 +129,20 @@
context.orCreateTestableResources.removeOverride(R.dimen.pixel_pitch)
}
+ @Test
+ fun testSetIgnoreDisplayTouches() =
+ testScope.runTest {
+ createUdfpsOverlayInteractor()
+ whenever(authController.isUdfpsSupported).thenReturn(true)
+ whenever(authController.udfpsProps).thenReturn(listOf(fingerprintSensorProperties))
+
+ underTest.setHandleTouches(false)
+ verify(fingerprintManager).setIgnoreDisplayTouches(anyLong(), anyInt(), eq(true))
+
+ underTest.setHandleTouches(true)
+ verify(fingerprintManager).setIgnoreDisplayTouches(anyLong(), anyInt(), eq(false))
+ }
+
private fun createUdfpsOverlayInteractor() {
underTest = kosmos.udfpsOverlayInteractor
testScope.runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
new file mode 100644
index 0000000..8820685
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ui.viewmodel
+
+import android.platform.test.annotations.EnableFlags
+import android.service.dream.dreamManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.statusbar.policy.batteryController
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+@RunWith(AndroidJUnit4::class)
+class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val underTest: CommunalToDreamButtonViewModel by lazy {
+ kosmos.communalToDreamButtonViewModel
+ }
+
+ @Before
+ fun setUp() {
+ kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+ underTest.activateIn(testScope)
+ }
+
+ @Test
+ fun shouldShowDreamButtonOnHub_trueWhenCanDream() =
+ with(kosmos) {
+ runTest {
+ whenever(dreamManager.canStartDreaming(any())).thenReturn(true)
+ whenever(batteryController.isPluggedIn()).thenReturn(true)
+
+ val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
+ assertThat(shouldShowButton).isTrue()
+ }
+ }
+
+ @Test
+ fun shouldShowDreamButtonOnHub_falseWhenCannotDream() =
+ with(kosmos) {
+ runTest {
+ whenever(dreamManager.canStartDreaming(any())).thenReturn(false)
+ whenever(batteryController.isPluggedIn()).thenReturn(true)
+
+ val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
+ assertThat(shouldShowButton).isFalse()
+ }
+ }
+
+ @Test
+ fun shouldShowDreamButtonOnHub_falseWhenNotPluggedIn() =
+ with(kosmos) {
+ runTest {
+ whenever(dreamManager.canStartDreaming(any())).thenReturn(true)
+ whenever(batteryController.isPluggedIn()).thenReturn(false)
+
+ val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
+ assertThat(shouldShowButton).isFalse()
+ }
+ }
+
+ @Test
+ fun onShowDreamButtonTap_startsDream() =
+ with(kosmos) {
+ runTest {
+ underTest.onShowDreamButtonTap()
+ runCurrent()
+
+ verify(dreamManager).startDream()
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModelTest.kt
index 755c4eb..ca7e203 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModelTest.kt
@@ -85,8 +85,7 @@
assertThat(actions).isNotEmpty()
assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
- assertThat(actions?.get(Swipe.Down))
- .isEqualTo(UserActionResult(Scenes.Shade, isIrreversible = true))
+ assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
setUpState(
isShadeTouchable = false,
@@ -103,8 +102,7 @@
assertThat(actions).isNotEmpty()
assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
- assertThat(actions?.get(Swipe.Down))
- .isEqualTo(UserActionResult(Scenes.Shade, isIrreversible = true))
+ assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
}
@Test
@@ -122,7 +120,7 @@
assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
assertThat(actions?.get(Swipe.Down))
- .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true))
+ .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade))
setUpState(
isShadeTouchable = false,
@@ -140,7 +138,7 @@
assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
assertThat(actions?.get(Swipe.Down))
- .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true))
+ .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade))
}
@Test
@@ -158,9 +156,7 @@
assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
assertThat(actions?.get(Swipe.Down))
- .isEqualTo(
- UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true)
- )
+ .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade))
setUpState(
isShadeTouchable = false,
@@ -174,9 +170,7 @@
assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
assertThat(actions?.get(Swipe.Down))
- .isEqualTo(
- UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true)
- )
+ .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade))
}
private fun TestScope.setUpState(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/util/ResizeUtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/util/ResizeUtilsTest.kt
new file mode 100644
index 0000000..b0f4803
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/util/ResizeUtilsTest.kt
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.model.CommunalContentSize
+import com.android.systemui.communal.util.ResizeUtils.resizeOngoingItems
+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
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ResizeUtilsTest : SysuiTestCase() {
+ private val mockWidget =
+ mock<CommunalContentModel.WidgetContent.Widget> {
+ on { size } doReturn CommunalContentSize.Responsive(1)
+ }
+
+ @Test
+ fun noOngoingContent() {
+ val list = listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 2)
+
+ assertThat(resized).containsExactly(mockWidget)
+ }
+
+ @Test
+ fun singleOngoingContent_singleRowGrid() {
+ val list = createOngoingListWithSize(1) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 1)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(CommunalContentSize.Responsive(1), mockWidget.size)
+ .inOrder()
+ }
+
+ @Test
+ fun singleOngoingContent_twoRowGrid() {
+ val list = createOngoingListWithSize(1) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 2)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(CommunalContentSize.Responsive(2), mockWidget.size)
+ .inOrder()
+ }
+
+ @Test
+ fun singleOngoingContent_threeRowGrid() {
+ val list = createOngoingListWithSize(1) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 3)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(
+ CommunalContentSize.Responsive(2),
+ CommunalContentSize.Responsive(1),
+ mockWidget.size,
+ )
+ .inOrder()
+ // A spacer should be added as the second element to avoid mixing widget content
+ // with ongoing content.
+ assertThat(resized[1]).isInstanceOf(CommunalContentModel.Spacer::class.java)
+ }
+
+ @Test
+ fun twoOngoingContent_singleRowGrid() {
+ val list = createOngoingListWithSize(2) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 1)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ mockWidget.size,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun twoOngoingContent_twoRowGrid() {
+ val list = createOngoingListWithSize(2) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 2)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ mockWidget.size,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun twoOngoingContent_threeRowGrid() {
+ val list = createOngoingListWithSize(2) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 3)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(
+ CommunalContentSize.Responsive(2),
+ CommunalContentSize.Responsive(1),
+ mockWidget.size,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun threeOngoingContent_singleRowGrid() {
+ val list = createOngoingListWithSize(3) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 1)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ mockWidget.size,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun threeOngoingContent_twoRowGrid() {
+ val list = createOngoingListWithSize(3) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 2)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(
+ CommunalContentSize.Responsive(2),
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ mockWidget.size,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun threeOngoingContent_threeRowGrid() {
+ val list = createOngoingListWithSize(3) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 3)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ mockWidget.size,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun fourOngoingContent_singleRowGrid() {
+ val list = createOngoingListWithSize(4) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 1)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ mockWidget.size,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun fourOngoingContent_twoRowGrid() {
+ val list = createOngoingListWithSize(4) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 2)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(1),
+ mockWidget.size,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun fourOngoingContent_threeRowGrid() {
+ val list = createOngoingListWithSize(4) + listOf(mockWidget)
+ val resized = resizeOngoingItems(list = list, numRows = 3)
+
+ assertThat(resized.map { it.size })
+ .containsExactly(
+ CommunalContentSize.Responsive(2),
+ CommunalContentSize.Responsive(1),
+ CommunalContentSize.Responsive(2),
+ CommunalContentSize.Responsive(1),
+ mockWidget.size,
+ )
+ .inOrder()
+ }
+
+ private fun createOngoingListWithSize(size: Int): List<CommunalContentModel.Ongoing> {
+ return List(size) { CommunalContentModel.Umo(createdTimestampMillis = 100) }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index 18cc8bf..5bbd3ff 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -172,16 +172,16 @@
}
@Test
- fun selectedKey_onReorderWidgets_isSet() =
+ fun selectedKey_onReorderWidgets_isCleared() =
testScope.runTest {
val selectedKey by collectLastValue(underTest.selectedKey)
- underTest.setSelectedKey(null)
- assertThat(selectedKey).isNull()
-
val key = CommunalContentModel.KEY.widget(123)
- underTest.onReorderWidgetStart(key)
+ underTest.setSelectedKey(key)
assertThat(selectedKey).isEqualTo(key)
+
+ underTest.onReorderWidgetStart()
+ assertThat(selectedKey).isNull()
}
@Test
@@ -234,7 +234,7 @@
@Test
fun reorderWidget_uiEventLogging_start() {
- underTest.onReorderWidgetStart(CommunalContentModel.KEY.widget(123))
+ underTest.onReorderWidgetStart()
verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_START)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayRegistrantTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayRegistrantTest.kt
index 790df03..1e937b4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayRegistrantTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayRegistrantTest.kt
@@ -29,8 +29,11 @@
import androidx.test.filters.SmallTest
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
+import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
import com.android.systemui.log.core.FakeLogBuffer
import com.android.systemui.shared.condition.Monitor
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.withArgCaptor
import kotlin.test.Test
import org.junit.Before
@@ -48,6 +51,8 @@
@TestableLooper.RunWithLooper
@RunWith(AndroidJUnit4::class)
class DreamOverlayRegistrantTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
private val context = mock<Context>()
private val packageManager = mock<PackageManager>()
@@ -73,6 +78,7 @@
monitor,
packageManager,
dreamManager,
+ kosmos.communalSettingsInteractor,
logBuffer,
)
@@ -117,7 +123,7 @@
/** Verify overlay registered when enabled in manifest. */
@Test
- @DisableFlags(Flags.FLAG_COMMUNAL_HUB_ON_MOBILE)
+ @DisableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
fun testRegisteredWhenEnabledWithManifest() {
serviceInfo.enabled = true
start()
@@ -127,8 +133,10 @@
/** Verify overlay registered for mobile hub with flag. */
@Test
- @EnableFlags(Flags.FLAG_COMMUNAL_HUB_ON_MOBILE)
+ @EnableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
fun testRegisteredForMobileHub() {
+ kosmos.setCommunalV2ConfigEnabled(true)
+
start()
verify(dreamManager).registerDreamOverlayService(componentName)
@@ -139,7 +147,7 @@
* enabled.
*/
@Test
- @DisableFlags(Flags.FLAG_COMMUNAL_HUB_ON_MOBILE)
+ @DisableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
fun testDisabledForMobileWithoutMobileHub() {
start()
@@ -154,8 +162,9 @@
/** Ensure service unregistered when component is disabled at runtime. */
@Test
- @EnableFlags(Flags.FLAG_COMMUNAL_HUB_ON_MOBILE)
+ @EnableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
fun testUnregisteredWhenComponentDisabled() {
+ kosmos.setCommunalV2ConfigEnabled(true)
start()
verify(dreamManager).registerDreamOverlayService(componentName)
clearInvocations(dreamManager)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index f924ccb..b07097d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -43,7 +43,7 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
-import com.android.systemui.Flags.FLAG_COMMUNAL_HUB_ON_MOBILE
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.SysuiTestCase
import com.android.systemui.ambient.touch.TouchHandler
@@ -55,7 +55,9 @@
import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
+import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.complication.ComplicationHostViewController
@@ -262,6 +264,7 @@
mKeyguardUpdateMonitor,
mScrimManager,
mCommunalInteractor,
+ kosmos.communalSettingsInteractor,
kosmos.sceneInteractor,
mSystemDialogsCloser,
mUiEventLogger,
@@ -1283,7 +1286,7 @@
environmentComponents.verifyNoMoreInteractions()
}
- @DisableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE)
+ @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
@Test
fun testAmbientTouchHandlersRegistration_registerHideComplicationAndCommunal() {
val client = client
@@ -1303,9 +1306,11 @@
.containsExactly(mHideComplicationTouchHandler, mCommunalTouchHandler)
}
- @EnableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE)
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
@Test
fun testAmbientTouchHandlersRegistration_v2_registerOnlyHideComplication() {
+ kosmos.setCommunalV2ConfigEnabled(true)
+
val client = client
// Inform the overlay service of dream starting.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt
index 55b87db..d6daa79 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt
@@ -86,8 +86,7 @@
)
assertThat(actions).isNotEmpty()
assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
- assertThat(actions?.get(Swipe.Down))
- .isEqualTo(UserActionResult(Scenes.Shade, isIrreversible = true))
+ assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
assertThat(actions?.get(Swipe.Start)).isNull()
assertThat(actions?.get(Swipe.End)).isNull()
@@ -105,8 +104,7 @@
)
assertThat(actions).isNotEmpty()
assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
- assertThat(actions?.get(Swipe.Down))
- .isEqualTo(UserActionResult(Scenes.Shade, isIrreversible = true))
+ assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
assertThat(actions?.get(Swipe.Start)).isNull()
assertThat(actions?.get(Swipe.End)).isNull()
}
@@ -127,7 +125,7 @@
assertThat(actions).isNotEmpty()
assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
assertThat(actions?.get(Swipe.Down))
- .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true))
+ .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade))
assertThat(actions?.get(Swipe.Start)).isNull()
assertThat(actions?.get(Swipe.End)).isNull()
@@ -146,7 +144,7 @@
assertThat(actions).isNotEmpty()
assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
assertThat(actions?.get(Swipe.Down))
- .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true))
+ .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade))
assertThat(actions?.get(Swipe.Start)).isNull()
assertThat(actions?.get(Swipe.End)).isNull()
}
@@ -167,9 +165,7 @@
assertThat(actions).isNotEmpty()
assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
assertThat(actions?.get(Swipe.Down))
- .isEqualTo(
- UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true)
- )
+ .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade))
assertThat(actions?.get(Swipe.Start)).isNull()
assertThat(actions?.get(Swipe.End)).isNull()
@@ -184,9 +180,7 @@
assertThat(actions).isNotEmpty()
assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
assertThat(actions?.get(Swipe.Down))
- .isEqualTo(
- UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true)
- )
+ .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade))
assertThat(actions?.get(Swipe.Start)).isNull()
assertThat(actions?.get(Swipe.End)).isNull()
}
@@ -206,8 +200,7 @@
)
assertThat(actions).isNotEmpty()
assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
- assertThat(actions?.get(Swipe.Down))
- .isEqualTo(UserActionResult(Scenes.Shade, isIrreversible = true))
+ assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
assertThat(actions?.get(Swipe.End)).isNull()
@@ -225,8 +218,7 @@
)
assertThat(actions).isNotEmpty()
assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
- assertThat(actions?.get(Swipe.Down))
- .isEqualTo(UserActionResult(Scenes.Shade, isIrreversible = true))
+ assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
assertThat(actions?.get(Swipe.End)).isNull()
}
@@ -247,7 +239,7 @@
assertThat(actions).isNotEmpty()
assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
assertThat(actions?.get(Swipe.Down))
- .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true))
+ .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade))
assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
assertThat(actions?.get(Swipe.End)).isNull()
@@ -266,7 +258,7 @@
assertThat(actions).isNotEmpty()
assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
assertThat(actions?.get(Swipe.Down))
- .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true))
+ .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade))
assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
assertThat(actions?.get(Swipe.End)).isNull()
}
@@ -287,9 +279,7 @@
assertThat(actions).isNotEmpty()
assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
assertThat(actions?.get(Swipe.Down))
- .isEqualTo(
- UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true)
- )
+ .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade))
assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
assertThat(actions?.get(Swipe.End)).isNull()
@@ -304,9 +294,7 @@
assertThat(actions).isNotEmpty()
assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
assertThat(actions?.get(Swipe.Down))
- .isEqualTo(
- UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true)
- )
+ .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade))
assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
assertThat(actions?.get(Swipe.End)).isNull()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt
index 76434ee..1de38ee 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt
@@ -16,10 +16,8 @@
package com.android.systemui.inputdevice.tutorial.ui.viewmodel
-import androidx.lifecycle.Lifecycle.Event
-import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -55,7 +53,6 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mockito.mock
import org.mockito.kotlin.mock
@OptIn(ExperimentalCoroutinesApi::class)
@@ -71,9 +68,6 @@
private var tutorialScope = INTENT_TUTORIAL_SCOPE_TOUCHPAD
private val viewModel by lazy { createViewModel(tutorialScope) }
- // createUnsafe so its methods don't have to be called on Main thread
- private val lifecycle = LifecycleRegistry.createUnsafe(mock(LifecycleOwner::class.java))
-
@get:Rule val mainDispatcherRule = MainDispatcherRule(kosmos.testDispatcher)
private fun createViewModel(
@@ -88,7 +82,6 @@
mock<InputDeviceTutorialLogger>(),
SavedStateHandle(mapOf(INTENT_TUTORIAL_SCOPE_KEY to scope)),
)
- lifecycle.addObserver(viewModel)
return viewModel
}
@@ -279,7 +272,7 @@
collectValues(viewModel.screen) // just to initialize viewModel
peripheralsState(touchpadConnected = true)
- lifecycle.handleLifecycleEvent(Event.ON_START)
+ viewModel.onStart(TestLifecycleOwner())
assertGesturesDisabled()
}
@@ -291,8 +284,8 @@
collectValues(viewModel.screen)
peripheralsState(touchpadConnected = true)
- lifecycle.handleLifecycleEvent(Event.ON_START)
- lifecycle.handleLifecycleEvent(Event.ON_STOP)
+ viewModel.onStart(TestLifecycleOwner())
+ viewModel.onStop(TestLifecycleOwner())
assertGesturesNotDisabled()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt
index f90ab1f..3bf59f3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt
@@ -40,6 +40,9 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyboard.shortcut.data.source.FakeKeyboardShortcutGroupsSource
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.CYCLE_BACK_THROUGH_RECENT_APPS_SHORTCUT_LABEL
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.CYCLE_FORWARD_THROUGH_RECENT_APPS_SHORTCUT_LABEL
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.recentAppsGroup
import com.android.systemui.keyboard.shortcut.defaultShortcutCategoriesRepository
import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
@@ -270,6 +273,31 @@
assertThat(systemCategory).isEqualTo(expectedCategory)
}
+ @Test
+ fun categories_recentAppsSwitchShortcutsIsMarkedNonCustomizable() {
+ testScope.runTest {
+ helper.setImeShortcuts(emptyList())
+ fakeSystemSource.setGroups(emptyList())
+ fakeMultiTaskingSource.setGroups(recentAppsGroup)
+
+ helper.showFromActivity()
+ val categories by collectLastValue(repo.categories)
+
+ val cycleForwardThroughRecentAppsShortcut =
+ categories?.first { it.type == ShortcutCategoryType.MultiTasking }
+ ?.subCategories?.first { it.label == recentAppsGroup.label }
+ ?.shortcuts?.first { it.label == CYCLE_FORWARD_THROUGH_RECENT_APPS_SHORTCUT_LABEL }
+
+ val cycleBackThroughRecentAppsShortcut =
+ categories?.first { it.type == ShortcutCategoryType.MultiTasking }
+ ?.subCategories?.first { it.label == recentAppsGroup.label }
+ ?.shortcuts?.first { it.label == CYCLE_BACK_THROUGH_RECENT_APPS_SHORTCUT_LABEL }
+
+ assertThat(cycleForwardThroughRecentAppsShortcut?.isCustomizable).isFalse()
+ assertThat(cycleBackThroughRecentAppsShortcut?.isCustomizable).isFalse()
+ }
+ }
+
private fun simpleSubCategory(vararg shortcuts: Shortcut) =
ShortcutSubCategory(simpleGroupLabel, shortcuts.asList())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapterTest.kt
new file mode 100644
index 0000000..f78c692
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapterTest.kt
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.keyboard.shortcut.data.repository
+
+import android.app.role.RoleManager
+import android.app.role.roleManager
+import android.content.Context
+import android.content.Intent
+import android.content.mockedContext
+import android.content.packageManager
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.hardware.input.AppLaunchData
+import android.hardware.input.AppLaunchData.RoleData
+import android.hardware.input.InputGestureData
+import android.hardware.input.InputGestureData.createKeyTrigger
+import android.view.KeyEvent.KEYCODE_A
+import android.view.KeyEvent.META_ALT_ON
+import android.view.KeyEvent.META_CTRL_ON
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.app.ResolverActivity
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyboard.shortcut.data.model.InternalGroupsSource
+import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutGroup
+import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutInfo
+import com.android.systemui.keyboard.shortcut.inputGestureDataAdapter
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.res.R
+import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class InputGestureDataAdapterTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos().also { kosmos ->
+ kosmos.userTracker = FakeUserTracker(onCreateCurrentUserContext = { kosmos.mockedContext })
+ }
+ private val adapter = kosmos.inputGestureDataAdapter
+ private val roleManager = kosmos.roleManager
+ private val packageManager: PackageManager = kosmos.packageManager
+ private val mockUserContext: Context = kosmos.mockedContext
+ private val intent: Intent = mock()
+ private val fakeResolverActivityInfo =
+ ActivityInfo().apply { name = ResolverActivity::class.qualifiedName }
+ private val fakeActivityInfo: ActivityInfo =
+ ActivityInfo().apply {
+ name = FAKE_ACTIVITY_NAME
+ icon = 0x1
+ nonLocalizedLabel = TEST_SHORTCUT_LABEL
+ }
+ private val mockSelectorIntent: Intent = mock()
+
+ @Before
+ fun setup() {
+ whenever(mockUserContext.packageManager).thenReturn(packageManager)
+ whenever(mockUserContext.getSystemService(RoleManager::class.java)).thenReturn(roleManager)
+ whenever(roleManager.isRoleAvailable(TEST_ROLE)).thenReturn(true)
+ whenever(roleManager.getDefaultApplication(TEST_ROLE)).thenReturn(TEST_ROLE_PACKAGE)
+ whenever(packageManager.getActivityInfo(any(), anyInt())).thenReturn(mock())
+ whenever(packageManager.getLaunchIntentForPackage(TEST_ROLE_PACKAGE)).thenReturn(intent)
+ whenever(intent.selector).thenReturn(mockSelectorIntent)
+ whenever(mockSelectorIntent.categories).thenReturn(setOf(TEST_ACTIVITY_CATEGORY))
+ }
+
+ @Test
+ fun shortcutLabel_whenDefaultAppForCategoryIsNotSet_loadsLabelFromFirstAppMatchingIntent() =
+ kosmos.runTest {
+ setApiToRetrieveResolverActivity()
+
+ val inputGestureData = buildInputGestureDataForAppLaunchShortcut()
+ val internalGroups = adapter.toInternalGroupSources(listOf(inputGestureData))
+ val label =
+ internalGroups.firstOrNull()?.groups?.firstOrNull()?.items?.firstOrNull()?.label
+
+ assertThat(label).isEqualTo(expectedShortcutLabelForFirstAppMatchingIntent)
+ }
+
+ @Test
+ fun shortcutLabel_whenDefaultAppForCategoryIsSet_loadsLabelOfDefaultApp() {
+ kosmos.runTest {
+ setApiToRetrieveSpecificActivity()
+
+ val inputGestureData = buildInputGestureDataForAppLaunchShortcut()
+ val internalGroups = adapter.toInternalGroupSources(listOf(inputGestureData))
+ val label =
+ internalGroups.firstOrNull()?.groups?.firstOrNull()?.items?.firstOrNull()?.label
+
+ assertThat(label).isEqualTo(TEST_SHORTCUT_LABEL)
+ }
+ }
+
+ @Test
+ fun shortcutIcon_whenDefaultAppForCategoryIsSet_loadsIconOfDefaultApp() {
+ kosmos.runTest {
+ setApiToRetrieveSpecificActivity()
+
+ val inputGestureData = buildInputGestureDataForAppLaunchShortcut()
+ val internalGroups = adapter.toInternalGroupSources(listOf(inputGestureData))
+ val icon =
+ internalGroups.firstOrNull()?.groups?.firstOrNull()?.items?.firstOrNull()?.icon
+
+ assertThat(icon).isNotNull()
+ }
+ }
+
+ @Test
+ fun internalGroupSource_isCorrectlyConvertedWithSimpleInputGestureData() =
+ kosmos.runTest {
+ setApiToRetrieveResolverActivity()
+
+ val inputGestureData = buildInputGestureDataForAppLaunchShortcut()
+ val internalGroups = adapter.toInternalGroupSources(listOf(inputGestureData))
+
+ assertThat(internalGroups).containsExactly(
+ InternalGroupsSource(
+ type = ShortcutCategoryType.AppCategories,
+ groups = listOf(
+ InternalKeyboardShortcutGroup(
+ label = APPLICATION_SHORTCUT_GROUP_LABEL,
+ items = listOf(
+ InternalKeyboardShortcutInfo(
+ label = expectedShortcutLabelForFirstAppMatchingIntent,
+ keycode = KEYCODE_A,
+ modifiers = META_CTRL_ON or META_ALT_ON,
+ isCustomShortcut = true
+ )
+ )
+ )
+ )
+ )
+ )
+ }
+
+ private fun setApiToRetrieveResolverActivity() {
+ whenever(intent.resolveActivityInfo(eq(packageManager), anyInt()))
+ .thenReturn(fakeResolverActivityInfo)
+ }
+
+ private fun setApiToRetrieveSpecificActivity() {
+ whenever(intent.resolveActivityInfo(eq(packageManager), anyInt()))
+ .thenReturn(fakeActivityInfo)
+ }
+
+
+ private fun buildInputGestureDataForAppLaunchShortcut(
+ keyCode: Int = KEYCODE_A,
+ modifiers: Int = META_CTRL_ON or META_ALT_ON,
+ appLaunchData: AppLaunchData = RoleData(TEST_ROLE)
+ ): InputGestureData {
+ return InputGestureData.Builder()
+ .setTrigger(createKeyTrigger(keyCode, modifiers))
+ .setAppLaunchData(appLaunchData)
+ .build()
+ }
+
+ private val expectedShortcutLabelForFirstAppMatchingIntent =
+ context.getString(R.string.keyboard_shortcut_group_applications_browser)
+
+ private companion object {
+ private const val TEST_ROLE = "Test Browser Role"
+ private const val TEST_ROLE_PACKAGE = "test.browser.package"
+ private const val APPLICATION_SHORTCUT_GROUP_LABEL = "Applications"
+ private const val FAKE_ACTIVITY_NAME = "Fake activity"
+ private const val TEST_SHORTCUT_LABEL = "Test shortcut label"
+ private const val TEST_ACTIVITY_CATEGORY = Intent.CATEGORY_APP_BROWSER
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
index 407bdf8..969dace 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
@@ -57,14 +57,14 @@
KeyboardShortcutInfo(
/* label = */ "Shortcut with repeated label",
/* keycode = */ KeyEvent.KEYCODE_H,
- /* modifiers = */ KeyEvent.META_META_ON,
+ /* modifiers = */ META_META_ON,
)
private val shortcutInfoWithRepeatedLabelAlternate =
KeyboardShortcutInfo(
/* label = */ shortcutInfoWithRepeatedLabel.label,
/* keycode = */ KeyEvent.KEYCODE_L,
- /* modifiers = */ KeyEvent.META_META_ON,
+ /* modifiers = */ META_META_ON,
)
private val shortcutInfoWithRepeatedLabelSecondAlternate =
@@ -74,6 +74,27 @@
/* modifiers = */ KeyEvent.META_SHIFT_ON,
)
+ private val ShortcutsWithDiffSizeOfKeys =
+ KeyboardShortcutInfo(
+ /* label = */ "Shortcuts with diff size of keys",
+ /* keycode = */ KeyEvent.KEYCODE_HOME,
+ /* modifiers = */ 0,
+ )
+
+ private val ShortcutsWithDiffSizeOfKeys2 =
+ KeyboardShortcutInfo(
+ /* label = */ ShortcutsWithDiffSizeOfKeys.label,
+ /* keycode = */ KeyEvent.KEYCODE_1,
+ /* modifiers = */ META_META_ON,
+ )
+
+ private val ShortcutsWithDiffSizeOfKeys3 =
+ KeyboardShortcutInfo(
+ /* label = */ ShortcutsWithDiffSizeOfKeys.label,
+ /* keycode = */ KeyEvent.KEYCODE_2,
+ /* modifiers = */ META_META_ON or META_FUNCTION_ON,
+ )
+
private val shortcutWithGroupedRepeatedLabel =
shortcut(shortcutInfoWithRepeatedLabel.label!!.toString()) {
command {
@@ -105,9 +126,44 @@
KeyboardShortcutInfo(
/* label = */ "Standard shortcut 1",
/* keycode = */ KeyEvent.KEYCODE_N,
- /* modifiers = */ KeyEvent.META_META_ON,
+ /* modifiers = */ META_META_ON,
)
+ const val CYCLE_FORWARD_THROUGH_RECENT_APPS_SHORTCUT_LABEL = "Cycle forward through recent apps"
+ const val CYCLE_BACK_THROUGH_RECENT_APPS_SHORTCUT_LABEL = "Cycle backward through recent apps"
+
+ private val recentAppsCycleForwardShortcutInfo =
+ KeyboardShortcutInfo(
+ /* label = */ CYCLE_FORWARD_THROUGH_RECENT_APPS_SHORTCUT_LABEL,
+ /* keycode = */ KeyEvent.KEYCODE_N,
+ /* modifiers = */ META_META_ON,
+ )
+
+ private val recentAppsCycleBackShortcutInfo =
+ KeyboardShortcutInfo(
+ /* label = */ CYCLE_BACK_THROUGH_RECENT_APPS_SHORTCUT_LABEL,
+ /* keycode = */ KeyEvent.KEYCODE_N,
+ /* modifiers = */ META_META_ON,
+ )
+
+ private val recentAppsCycleForwardShortcut =
+ shortcut(recentAppsCycleForwardShortcutInfo.label!!.toString()) {
+ command {
+ key(R.drawable.ic_ksh_key_meta)
+ key("N")
+ }
+ isCustomizable = false
+ }
+
+ private val recentAppsCycleBackShortcut =
+ shortcut(recentAppsCycleBackShortcutInfo.label!!.toString()) {
+ command {
+ key(R.drawable.ic_ksh_key_meta)
+ key("N")
+ }
+ isCustomizable = false
+ }
+
private val standardShortcut1 =
shortcut(standardShortcutInfo1.label!!.toString()) {
command {
@@ -165,7 +221,7 @@
KeyboardShortcutInfo(
/* label = */ "Shortcut with unsupported modifiers",
/* keycode = */ KeyEvent.KEYCODE_A,
- /* modifiers = */ KeyEvent.META_META_ON or KeyEvent.KEYCODE_SPACE,
+ /* modifiers = */ META_META_ON or KeyEvent.KEYCODE_SPACE,
)
private val groupWithRepeatedShortcutLabels =
@@ -241,6 +297,12 @@
listOf(standardShortcut3),
)
+ val recentAppsGroup =
+ KeyboardShortcutGroup(
+ "Recent apps",
+ listOf(recentAppsCycleForwardShortcutInfo, recentAppsCycleBackShortcutInfo),
+ )
+
private val standardGroup1 =
KeyboardShortcutGroup(
"Standard group 1",
@@ -259,6 +321,12 @@
private val standardSystemAppSubcategoryWithCustomHomeShortcut =
ShortcutSubCategory("System controls", listOf(customGoHomeShortcut))
+ private val recentAppsSubCategory =
+ ShortcutSubCategory(
+ recentAppsGroup.label!!.toString(),
+ listOf(recentAppsCycleForwardShortcut, recentAppsCycleBackShortcut),
+ )
+
private val standardSubCategory1 =
ShortcutSubCategory(
standardGroup1.label!!.toString(),
@@ -354,6 +422,9 @@
),
)
+ val multitaskingCategoryWithRecentAppsGroup =
+ ShortcutCategory(type = MultiTasking, subCategories = listOf(recentAppsSubCategory))
+
val multitaskingGroups = listOf(standardGroup2, standardGroup1)
val multitaskingCategory =
ShortcutCategory(
@@ -381,6 +452,16 @@
groupWithSupportedAndUnsupportedModifierShortcut,
)
+ val groupWithDifferentSizeOfShortcutKeys =
+ KeyboardShortcutGroup(
+ "Group with different size of shortcut keys",
+ listOf(
+ ShortcutsWithDiffSizeOfKeys3,
+ ShortcutsWithDiffSizeOfKeys,
+ ShortcutsWithDiffSizeOfKeys2,
+ ),
+ )
+
val subCategoriesWithUnsupportedModifiersRemoved =
listOf(subCategoryWithStandardShortcut, subCategoryWithUnsupportedShortcutsRemoved)
@@ -408,6 +489,7 @@
category: ShortcutCategoryType,
subcategoryLabel: String,
shortcutLabel: String,
+ includeInCustomization: Boolean = true,
): ShortcutCategory {
return ShortcutCategory(
type = category,
@@ -415,13 +497,13 @@
listOf(
ShortcutSubCategory(
label = subcategoryLabel,
- shortcuts = listOf(simpleShortcut(shortcutLabel)),
+ shortcuts = listOf(simpleShortcut(shortcutLabel, includeInCustomization)),
)
),
)
}
- private fun simpleShortcut(label: String) =
+ private fun simpleShortcut(label: String, includeInCustomization: Boolean = true) =
Shortcut(
label = label,
commands =
@@ -436,6 +518,7 @@
),
)
),
+ isCustomizable = includeInCustomization,
)
val customizableInputGestureWithUnknownKeyGestureType =
@@ -460,12 +543,12 @@
simpleShortcutCategory(
MultiTasking,
"Split screen",
- "Switch from split screen to full screen",
+ "Switch to full screen",
),
simpleShortcutCategory(
MultiTasking,
"Split screen",
- "Use split screen with current app on the left",
+ "Use split screen with app on the left",
),
simpleShortcutCategory(
MultiTasking,
@@ -475,7 +558,7 @@
simpleShortcutCategory(
MultiTasking,
"Split screen",
- "Use split screen with current app on the right",
+ "Use split screen with app on the right",
),
simpleShortcutCategory(
MultiTasking,
@@ -484,13 +567,6 @@
),
simpleShortcutCategory(System, "System controls", "Show shortcuts"),
simpleShortcutCategory(System, "System controls", "View recent apps"),
- simpleShortcutCategory(AppCategories, "Applications", "Calculator"),
- simpleShortcutCategory(AppCategories, "Applications", "Calendar"),
- simpleShortcutCategory(AppCategories, "Applications", "Browser"),
- simpleShortcutCategory(AppCategories, "Applications", "Contacts"),
- simpleShortcutCategory(AppCategories, "Applications", "Email"),
- simpleShortcutCategory(AppCategories, "Applications", "Maps"),
- simpleShortcutCategory(AppCategories, "Applications", "SMS"),
)
val customInputGestureTypeHome = simpleInputGestureData(keyGestureType = KEY_GESTURE_TYPE_HOME)
@@ -532,27 +608,6 @@
),
simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS),
simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS
- ),
- simpleInputGestureData(
- keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING
- ),
- simpleInputGestureData(
keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER
),
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
index c3cedba..8f0bc64 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
@@ -369,6 +369,20 @@
}
}
+ @Test
+ fun categories_showShortcutsInAscendingOrderOfKeySize() =
+ testScope.runTest {
+ systemShortcutsSource.setGroups(TestShortcuts.groupWithDifferentSizeOfShortcutKeys)
+ val categories by collectLastValue(interactor.shortcutCategories)
+
+ helper.showFromActivity()
+
+ val systemCategoryShortcuts = categories?.get(0)?.subCategories?.get(0)?.shortcuts
+ val shortcutKeyCount =
+ systemCategoryShortcuts?.flatMap { it.commands }?.map { it.keys.size }
+ assertThat(shortcutKeyCount).containsExactly(1, 2, 3).inOrder()
+ }
+
private fun setCustomInputGestures(customInputGestures: List<InputGestureData>) {
whenever(fakeInputManager.inputManager.getCustomInputGestures(/* filter= */ anyOrNull()))
.thenReturn(customInputGestures)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigTest.kt
index 77c615c..789b10b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigTest.kt
@@ -25,7 +25,8 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.communalSceneRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
-import com.android.systemui.communal.domain.interactor.setCommunalEnabled
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
+import com.android.systemui.communal.domain.interactor.setCommunalV2Enabled
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.andSceneContainer
@@ -38,7 +39,6 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -48,7 +48,7 @@
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
-@EnableFlags(Flags.FLAG_GLANCEABLE_HUB_SHORTCUT_BUTTON)
+@EnableFlags(Flags.FLAG_GLANCEABLE_HUB_SHORTCUT_BUTTON, Flags.FLAG_GLANCEABLE_HUB_V2)
@RunWith(ParameterizedAndroidJunit4::class)
class GlanceableHubQuickAffordanceConfigTest(flags: FlagsParameterization?) : SysuiTestCase() {
private val kosmos = testKosmos()
@@ -69,6 +69,7 @@
context = context,
communalInteractor = kosmos.communalInteractor,
communalSceneRepository = kosmos.communalSceneRepository,
+ communalSettingsInteractor = kosmos.communalSettingsInteractor,
sceneInteractor = kosmos.sceneInteractor,
)
}
@@ -76,28 +77,30 @@
@Test
fun lockscreenState_whenGlanceableHubEnabled_returnsVisible() =
testScope.runTest {
- kosmos.setCommunalEnabled(true)
+ kosmos.setCommunalV2Enabled(true)
runCurrent()
val lockScreenState by collectLastValue(underTest.lockScreenState)
- assertTrue(lockScreenState is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
+ assertThat(lockScreenState)
+ .isInstanceOf(KeyguardQuickAffordanceConfig.LockScreenState.Visible::class.java)
}
@Test
fun lockscreenState_whenGlanceableHubDisabled_returnsHidden() =
testScope.runTest {
- kosmos.setCommunalEnabled(false)
+ kosmos.setCommunalV2Enabled(false)
val lockScreenState by collectLastValue(underTest.lockScreenState)
runCurrent()
- assertTrue(lockScreenState is KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+ assertThat(lockScreenState)
+ .isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
}
@Test
fun pickerScreenState_whenGlanceableHubEnabled_returnsDefault() =
testScope.runTest {
- kosmos.setCommunalEnabled(true)
+ kosmos.setCommunalV2Enabled(true)
runCurrent()
assertThat(underTest.getPickerScreenState())
@@ -107,7 +110,7 @@
@Test
fun pickerScreenState_whenGlanceableHubDisabled_returnsDisabled() =
testScope.runTest {
- kosmos.setCommunalEnabled(false)
+ kosmos.setCommunalV2Enabled(false)
runCurrent()
assertThat(
@@ -143,7 +146,8 @@
@Parameters(name = "{0}")
fun getParams(): List<FlagsParameterization> {
return FlagsParameterization.allCombinationsOf(
- Flags.FLAG_GLANCEABLE_HUB_SHORTCUT_BUTTON
+ Flags.FLAG_GLANCEABLE_HUB_SHORTCUT_BUTTON,
+ Flags.FLAG_GLANCEABLE_HUB_V2,
)
.andSceneContainer()
}
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 e079619..635f8026 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
@@ -995,12 +995,12 @@
kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen))
val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
- sendSteps(sendStep1)
- kosmos.setSceneTransition(Idle(Scenes.Gone))
val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
+ sendSteps(sendStep1, sendStep2)
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
val sendStep3 = TransitionStep(UNDEFINED, AOD, 0f, STARTED)
val sendStep4 = TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)
- sendSteps(sendStep2, sendStep3, sendStep4)
+ sendSteps(sendStep3, sendStep4)
assertEquals(listOf<TransitionStep>(), currentStatesMapped)
}
@@ -1134,6 +1134,63 @@
)
}
+ @Test
+ @EnableSceneContainer
+ fun transition_filter_on_belongsToInstantReversedTransition_out_of_lockscreen_scene() =
+ testScope.runTest {
+ val currentStatesMapped by
+ collectValues(underTest.transition(Edge.create(LOCKSCREEN, Scenes.Gone)))
+
+ kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen))
+ val sendStep1 = TransitionStep(UNDEFINED, LOCKSCREEN, 0f, STARTED)
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
+ val sendStep2 = TransitionStep(UNDEFINED, LOCKSCREEN, 0.6f, CANCELED)
+ sendSteps(sendStep1, sendStep2)
+ val sendStep3 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+ val sendStep4 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
+ sendSteps(sendStep3, sendStep4)
+
+ assertEquals(listOf(sendStep3, sendStep4), currentStatesMapped)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun transition_filter_on_belongsToInstantReversedTransition_into_lockscreen_scene() =
+ testScope.runTest {
+ val currentStatesMapped by
+ collectValues(underTest.transition(Edge.create(Scenes.Gone, LOCKSCREEN)))
+
+ kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone))
+ val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+ kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
+ val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 0.6f, CANCELED)
+ sendSteps(sendStep1, sendStep2)
+ val sendStep3 = TransitionStep(UNDEFINED, LOCKSCREEN, 0f, STARTED)
+ val sendStep4 = TransitionStep(UNDEFINED, LOCKSCREEN, 1f, FINISHED)
+ sendSteps(sendStep3, sendStep4)
+
+ assertEquals(listOf(sendStep3, sendStep4), currentStatesMapped)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun transition_filter_on_belongsToInstantReversedTransition_out_of_ls_with_wildcard() =
+ testScope.runTest {
+ val currentStatesMapped by
+ collectValues(underTest.transition(Edge.create(to = LOCKSCREEN)))
+
+ kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone))
+ val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+ kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
+ val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 0.6f, CANCELED)
+ sendSteps(sendStep1, sendStep2)
+ val sendStep3 = TransitionStep(UNDEFINED, LOCKSCREEN, 0f, STARTED)
+ val sendStep4 = TransitionStep(UNDEFINED, LOCKSCREEN, 1f, FINISHED)
+ sendSteps(sendStep3, sendStep4)
+
+ assertEquals(listOf(sendStep3, sendStep4), currentStatesMapped)
+ }
+
private suspend fun sendSteps(vararg steps: TransitionStep) {
steps.forEach {
repository.sendTransitionStep(it)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt
index 62cc763..97e6763 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt
@@ -306,8 +306,7 @@
// Top edge is not applicable in dual shade, as well as two-finger swipe.
assertThat(downDestination).isNull()
} else {
- assertThat(downDestination)
- .isEqualTo(ShowOverlay(Overlays.NotificationsShade, isIrreversible = true))
+ assertThat(downDestination).isEqualTo(ShowOverlay(Overlays.NotificationsShade))
assertThat(downDestination?.transitionKey).isNull()
}
@@ -323,7 +322,7 @@
downWithTwoPointers -> assertThat(downFromTopRightDestination).isNull()
else -> {
assertThat(downFromTopRightDestination)
- .isEqualTo(ShowOverlay(Overlays.QuickSettingsShade, isIrreversible = true))
+ .isEqualTo(ShowOverlay(Overlays.QuickSettingsShade))
assertThat(downFromTopRightDestination?.transitionKey).isNull()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/kosmos/GeneralKosmosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/kosmos/GeneralKosmosTest.kt
new file mode 100644
index 0000000..49e553b
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/kosmos/GeneralKosmosTest.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kosmos
+
+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 kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GeneralKosmosTest : SysuiTestCase() {
+ @Test
+ fun stateCurrentValueMutableStateFlow() = runTest {
+ val source = MutableStateFlow(1)
+ val mapped =
+ source
+ .map { it * 2 }
+ .stateIn(
+ scope = backgroundScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = source.value * 2,
+ )
+ assertThat(currentValue(mapped)).isEqualTo(2)
+
+ source.value = 3
+ assertThat(currentValue(mapped)).isEqualTo(6)
+ }
+
+ @Test
+ fun stateCurrentValueOnEmittedFlow() = runTest {
+ val source = flow {
+ emit(1)
+ emit(2)
+ }
+ val mapped =
+ source
+ .map { it * 2 }
+ .stateIn(
+ scope = backgroundScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = 2,
+ )
+ assertThat(currentValue(mapped)).isEqualTo(4)
+ }
+
+ @Test
+ fun currentValueIsNull() = runTest {
+ val source = MutableStateFlow<Int?>(null)
+ assertThat(currentValue(source)).isEqualTo(null)
+ }
+}
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
index 0122028..9edd62a 100644
--- 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
@@ -99,14 +99,14 @@
assertThat(playerModel).isNotNull()
assertThat(playerModel?.titleName).isEqualTo(TITLE)
assertThat(playerModel?.artistName).isEqualTo(ARTIST)
- assertThat(underTest.isNewPlayer(playerModel!!)).isTrue()
+ assertThat(underTest.setPlayer(playerModel!!)).isTrue()
mediaDataFilter.onMediaDataLoaded(KEY, KEY, mediaData)
assertThat(playerModel).isNotNull()
assertThat(playerModel?.titleName).isEqualTo(TITLE)
assertThat(playerModel?.artistName).isEqualTo(ARTIST)
- assertThat(underTest.isNewPlayer(playerModel!!)).isFalse()
+ assertThat(underTest.setPlayer(playerModel!!)).isFalse()
}
@Test
@@ -120,7 +120,7 @@
assertThat(playerModel).isNotNull()
assertThat(playerModel?.titleName).isEqualTo(TITLE)
assertThat(playerModel?.artistName).isEqualTo(ARTIST)
- assertThat(underTest.isNewPlayer(playerModel!!)).isTrue()
+ assertThat(underTest.setPlayer(playerModel!!)).isTrue()
mediaData = initMediaData(ARTIST_2, TITLE_2)
@@ -129,7 +129,7 @@
assertThat(playerModel).isNotNull()
assertThat(playerModel?.titleName).isEqualTo(TITLE_2)
assertThat(playerModel?.artistName).isEqualTo(ARTIST_2)
- assertThat(underTest.isNewPlayer(playerModel!!)).isTrue()
+ assertThat(underTest.setPlayer(playerModel!!)).isTrue()
}
private fun initMediaData(artist: String, title: String): MediaData {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
index 111b3b6..4457d9b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
@@ -38,6 +38,7 @@
import com.android.systemui.qs.panels.domain.interactor.tileSquishinessInteractor
import com.android.systemui.qs.panels.ui.viewmodel.setConfigurationForMediaInRow
import com.android.systemui.res.R
+import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.largeScreenHeaderHelper
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
@@ -436,6 +437,28 @@
}
}
+ @Test
+ fun qsVisibleAndAnyShadeVisible() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ underTest.isQsVisible = false
+ fakeShadeRepository.setLegacyExpandedOrAwaitingInputTransfer(false)
+ assertThat(underTest.isQsVisibleAndAnyShadeExpanded).isFalse()
+
+ underTest.isQsVisible = true
+ fakeShadeRepository.setLegacyExpandedOrAwaitingInputTransfer(false)
+ assertThat(underTest.isQsVisibleAndAnyShadeExpanded).isFalse()
+
+ underTest.isQsVisible = false
+ fakeShadeRepository.setLegacyExpandedOrAwaitingInputTransfer(true)
+ assertThat(underTest.isQsVisibleAndAnyShadeExpanded).isFalse()
+
+ underTest.isQsVisible = true
+ fakeShadeRepository.setLegacyExpandedOrAwaitingInputTransfer(true)
+ assertThat(underTest.isQsVisibleAndAnyShadeExpanded).isTrue()
+ }
+ }
+
private fun TestScope.setMediaState(state: MediaState) {
with(kosmos) {
val activeMedia = state == ACTIVE_MEDIA
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 09a6c2c..1899b7d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -44,6 +44,7 @@
import android.text.TextUtils;
import android.util.ArraySet;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -384,6 +385,7 @@
return mSpec;
}
+ @NonNull
@Override
public State getState() {
return mState;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepositoryTest.kt
new file mode 100644
index 0000000..eef195b
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepositoryTest.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
+import com.android.systemui.qs.panels.shared.model.PaginatedGridLayoutType
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GridLayoutTypeRepositoryTest : SysuiTestCase() {
+ val kosmos = testKosmos()
+
+ val underTest = kosmos.gridLayoutTypeRepository
+
+ @Test
+ fun defaultType_paginated() =
+ kosmos.runTest {
+ val type by collectLastValue(underTest.defaultLayoutType)
+
+ assertThat(type).isEqualTo(PaginatedGridLayoutType)
+ }
+
+ @Test
+ fun dualShadeType_infinite() =
+ kosmos.runTest {
+ val type by collectLastValue(underTest.dualShadeLayoutType)
+
+ assertThat(type).isEqualTo(InfiniteGridLayoutType)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorTest.kt
new file mode 100644
index 0000000..b591538
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorTest.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.domain.interactor
+
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
+import com.android.systemui.qs.panels.shared.model.PaginatedGridLayoutType
+import com.android.systemui.shade.data.repository.fakeShadeRepository
+import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GridLayoutTypeInteractorTest : SysuiTestCase() {
+ val kosmos = testKosmos()
+
+ val Kosmos.underTest by Kosmos.Fixture { kosmos.gridLayoutTypeInteractor }
+
+ @DisableFlags(DualShade.FLAG_NAME)
+ @Test
+ fun noDualShade_gridAlwaysPaginated() =
+ kosmos.runTest {
+ val type by collectLastValue(underTest.layout)
+
+ fakeShadeRepository.setShadeLayoutWide(false)
+ assertThat(type).isEqualTo(PaginatedGridLayoutType)
+
+ fakeShadeRepository.setShadeLayoutWide(true)
+ assertThat(type).isEqualTo(PaginatedGridLayoutType)
+ }
+
+ @EnableFlags(DualShade.FLAG_NAME)
+ @Test
+ fun dualShade_gridAlwaysInfinite() =
+ kosmos.runTest {
+ val type by collectLastValue(underTest.layout)
+
+ fakeShadeRepository.setShadeLayoutWide(false)
+ assertThat(type).isEqualTo(InfiniteGridLayoutType)
+
+ fakeShadeRepository.setShadeLayoutWide(true)
+ assertThat(type).isEqualTo(InfiniteGridLayoutType)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayoutTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayoutTest.kt
index a9a527f..4891c9f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayoutTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayoutTest.kt
@@ -22,7 +22,7 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepository
import com.android.systemui.qs.panels.data.repository.defaultLargeTilesRepository
-import com.android.systemui.qs.panels.domain.interactor.infiniteGridLayout
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.infiniteGridLayout
import com.android.systemui.qs.panels.ui.viewmodel.MockTileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.testKosmos
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModelTest.kt
similarity index 95%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModelTest.kt
index f2bfd72..a8e390c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModelTest.kt
@@ -23,6 +23,7 @@
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
+import com.android.systemui.qs.panels.ui.viewmodel.toolbar.editModeButtonViewModelFactory
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
index 7a99aef..b921ff7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
@@ -18,8 +18,9 @@
import android.net.ConnectivityManager
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.telephony.flags.Flags
@@ -31,8 +32,11 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.settings.GlobalSettings
@@ -49,39 +53,34 @@
import org.mockito.kotlin.any
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
-class AirplaneModeTileTest : SysuiTestCase() {
+class AirplaneModeTileTest(flags: FlagsParameterization) : SysuiTestCase() {
- @Mock
- private lateinit var mHost: QSHost
- @Mock
- private lateinit var mMetricsLogger: MetricsLogger
- @Mock
- private lateinit var mStatusBarStateController: StatusBarStateController
- @Mock
- private lateinit var mActivityStarter: ActivityStarter
- @Mock
- private lateinit var mQsLogger: QSLogger
- @Mock
- private lateinit var mBroadcastDispatcher: BroadcastDispatcher
- @Mock
- private lateinit var mLazyConnectivityManager: Lazy<ConnectivityManager>
- @Mock
- private lateinit var mConnectivityManager: ConnectivityManager
- @Mock
- private lateinit var mGlobalSettings: GlobalSettings
- @Mock
- private lateinit var mUserTracker: UserTracker
- @Mock
- private lateinit var mUiEventLogger: QsEventLogger
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ @Mock private lateinit var mHost: QSHost
+ @Mock private lateinit var mMetricsLogger: MetricsLogger
+ @Mock private lateinit var mStatusBarStateController: StatusBarStateController
+ @Mock private lateinit var mActivityStarter: ActivityStarter
+ @Mock private lateinit var mQsLogger: QSLogger
+ @Mock private lateinit var mBroadcastDispatcher: BroadcastDispatcher
+ @Mock private lateinit var mLazyConnectivityManager: Lazy<ConnectivityManager>
+ @Mock private lateinit var mConnectivityManager: ConnectivityManager
+ @Mock private lateinit var mGlobalSettings: GlobalSettings
+ @Mock private lateinit var mUserTracker: UserTracker
+ @Mock private lateinit var mUiEventLogger: QsEventLogger
private lateinit var mTestableLooper: TestableLooper
private lateinit var mTile: AirplaneModeTile
- @Mock
- private lateinit var mClickJob: Job
+ @Mock private lateinit var mClickJob: Job
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -89,20 +88,22 @@
Mockito.`when`(mHost.context).thenReturn(mContext)
Mockito.`when`(mHost.userContext).thenReturn(mContext)
Mockito.`when`(mLazyConnectivityManager.get()).thenReturn(mConnectivityManager)
- mTile = AirplaneModeTile(
- mHost,
- mUiEventLogger,
- mTestableLooper.looper,
- Handler(mTestableLooper.looper),
- FalsingManagerFake(),
- mMetricsLogger,
- mStatusBarStateController,
- mActivityStarter,
- mQsLogger,
- mBroadcastDispatcher,
- mLazyConnectivityManager,
- mGlobalSettings,
- mUserTracker)
+ mTile =
+ AirplaneModeTile(
+ mHost,
+ mUiEventLogger,
+ mTestableLooper.looper,
+ Handler(mTestableLooper.looper),
+ FalsingManagerFake(),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQsLogger,
+ mBroadcastDispatcher,
+ mLazyConnectivityManager,
+ mGlobalSettings,
+ mUserTracker,
+ )
}
@After
@@ -117,8 +118,7 @@
mTile.handleUpdateState(state, 0)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_airplane_icon_off))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_airplane_icon_off))
}
@Test
@@ -127,8 +127,7 @@
mTile.handleUpdateState(state, 1)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_airplane_icon_on))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_airplane_icon_on))
}
@Test
@@ -150,4 +149,20 @@
verify(mConnectivityManager, times(0)).setAirplaneMode(any())
}
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
index d6be314..cf9ef4d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
@@ -18,9 +18,10 @@
import android.content.Context
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.systemui.SysuiTestCase
@@ -31,8 +32,11 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.util.settings.FakeSettings
@@ -49,14 +53,26 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
-class BatterySaverTileTest : SysuiTestCase() {
+class BatterySaverTileTest(flagsParameterization: FlagsParameterization) : SysuiTestCase() {
companion object {
private const val USER = 10
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flagsParameterization)
}
@Mock private lateinit var userContext: Context
@@ -94,7 +110,7 @@
activityStarter,
qsLogger,
batteryController,
- secureSettings
+ secureSettings,
)
tile.initialize()
@@ -150,8 +166,7 @@
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_battery_saver_icon_off))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_battery_saver_icon_off))
}
@Test
@@ -161,7 +176,14 @@
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_battery_saver_icon_on))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_battery_saver_icon_on))
+ }
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
index 093cdf2..6bb6713 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
@@ -17,13 +17,13 @@
package com.android.systemui.qs.tiles
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.provider.Settings
import android.safetycenter.SafetyCenterManager
import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
@@ -31,8 +31,12 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLoggerFake
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.google.common.truth.Truth.assertThat
@@ -41,37 +45,40 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
-class CameraToggleTileTest : SysuiTestCase() {
+class CameraToggleTileTest(flags: FlagsParameterization) : SysuiTestCase() {
companion object {
/* isBlocked */
const val CAMERA_TOGGLE_ENABLED: Boolean = false
const val CAMERA_TOGGLE_DISABLED: Boolean = true
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
}
- @Mock
- private lateinit var host: QSHost
- @Mock
- private lateinit var metricsLogger: MetricsLogger
- @Mock
- private lateinit var statusBarStateController: StatusBarStateController
- @Mock
- private lateinit var activityStarter: ActivityStarter
- @Mock
- private lateinit var qsLogger: QSLogger
- @Mock
- private lateinit var privacyController: IndividualSensorPrivacyController
- @Mock
- private lateinit var keyguardStateController: KeyguardStateController
- @Mock
- private lateinit var uiEventLogger: QsEventLoggerFake
- @Mock
- private lateinit var safetyCenterManager: SafetyCenterManager
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ @Mock private lateinit var host: QSHost
+ @Mock private lateinit var metricsLogger: MetricsLogger
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var qsLogger: QSLogger
+ @Mock private lateinit var privacyController: IndividualSensorPrivacyController
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var uiEventLogger: QsEventLoggerFake
+ @Mock private lateinit var safetyCenterManager: SafetyCenterManager
private lateinit var testableLooper: TestableLooper
private lateinit var tile: CameraToggleTile
@@ -82,7 +89,8 @@
testableLooper = TestableLooper.get(this)
whenever(host.context).thenReturn(mContext)
- tile = CameraToggleTile(
+ tile =
+ CameraToggleTile(
host,
uiEventLogger,
testableLooper.looper,
@@ -94,7 +102,8 @@
qsLogger,
privacyController,
keyguardStateController,
- safetyCenterManager)
+ safetyCenterManager,
+ )
}
@After
@@ -109,8 +118,7 @@
tile.handleUpdateState(state, CAMERA_TOGGLE_ENABLED)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_camera_access_icon_on))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_camera_access_icon_on))
}
@Test
@@ -119,14 +127,14 @@
tile.handleUpdateState(state, CAMERA_TOGGLE_DISABLED)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_camera_access_icon_off))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_camera_access_icon_off))
}
@Test
fun testLongClickIntent_safetyCenterEnabled() {
whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(true)
- val cameraTile = CameraToggleTile(
+ val cameraTile =
+ CameraToggleTile(
host,
uiEventLogger,
testableLooper.looper,
@@ -138,7 +146,8 @@
qsLogger,
privacyController,
keyguardStateController,
- safetyCenterManager)
+ safetyCenterManager,
+ )
assertThat(cameraTile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_CONTROLS)
cameraTile.destroy()
testableLooper.processAllMessages()
@@ -147,7 +156,8 @@
@Test
fun testLongClickIntent_safetyCenterDisabled() {
whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(false)
- val cameraTile = CameraToggleTile(
+ val cameraTile =
+ CameraToggleTile(
host,
uiEventLogger,
testableLooper.looper,
@@ -159,9 +169,18 @@
qsLogger,
privacyController,
keyguardStateController,
- safetyCenterManager)
+ safetyCenterManager,
+ )
assertThat(tile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_SETTINGS)
cameraTile.destroy()
testableLooper.processAllMessages()
}
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
index 1343527..a58dd63 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
@@ -16,6 +16,10 @@
package com.android.systemui.qs.tiles;
+import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
+
+import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -25,10 +29,10 @@
import android.content.Intent;
import android.os.Handler;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.provider.Settings;
import android.testing.TestableLooper;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
@@ -39,6 +43,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.flags.QsInCompose;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.res.R;
@@ -54,13 +59,23 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidJUnit4.class)
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class ColorInversionTileTest extends SysuiTestCase {
private static final Integer COLOR_INVERSION_DISABLED = 0;
private static final Integer COLOR_INVERSION_ENABLED = 1;
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
+ }
+
@Mock
private QSHost mHost;
@Mock
@@ -80,6 +95,11 @@
private SecureSettings mSecureSettings;
private ColorInversionTile mTile;
+ public ColorInversionTileTest(FlagsParameterization flags) {
+ super();
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -133,7 +153,7 @@
mTile.handleUpdateState(state, COLOR_INVERSION_DISABLED);
assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_invert_colors_icon_off));
+ .isEqualTo(createExpectedIcon(R.drawable.qs_invert_colors_icon_off));
}
@Test
@@ -143,6 +163,14 @@
mTile.handleUpdateState(state, COLOR_INVERSION_ENABLED);
assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_invert_colors_icon_on));
+ .isEqualTo(createExpectedIcon(R.drawable.qs_invert_colors_icon_on));
+ }
+
+ private QSTile.Icon createExpectedIcon(int resId) {
+ if (QsInCompose.isEnabled()) {
+ return new QSTileImpl.DrawableIconWithRes(mContext.getDrawable(resId), resId);
+ } else {
+ return QSTileImpl.ResourceIcon.get(resId);
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
index 73ae4ee..27fd281 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
@@ -17,8 +17,9 @@
package com.android.systemui.qs.tiles
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.systemui.SysuiTestCase
@@ -29,8 +30,11 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.DataSaverController
@@ -42,11 +46,17 @@
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
-class DataSaverTileTest : SysuiTestCase() {
+class DataSaverTileTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
@Mock private lateinit var mHost: QSHost
@Mock private lateinit var mMetricsLogger: MetricsLogger
@@ -84,7 +94,7 @@
mQsLogger,
dataSaverController,
mDialogTransitionAnimator,
- systemUIDialogFactory
+ systemUIDialogFactory,
)
}
@@ -100,8 +110,7 @@
tile.handleUpdateState(state, true)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_data_saver_icon_on))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_data_saver_icon_on))
}
@Test
@@ -110,7 +119,22 @@
tile.handleUpdateState(state, false)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_data_saver_icon_off))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_data_saver_icon_off))
+ }
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index 33748b9..9e4cf94 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -20,11 +20,14 @@
import android.content.Context
import android.content.Intent
import android.os.Handler
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.provider.Settings
import android.service.quicksettings.Tile
import android.testing.TestableLooper
import androidx.lifecycle.LifecycleOwner
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.systemui.SysuiTestCase
@@ -43,7 +46,9 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.res.R
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
@@ -67,11 +72,17 @@
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class DeviceControlsTileTest : SysuiTestCase() {
+class DeviceControlsTileTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
@Mock private lateinit var qsHost: QSHost
@Mock private lateinit var metricsLogger: MetricsLogger
@@ -151,7 +162,7 @@
}
`when`(controlsComponent.getTileTitleId()).thenReturn(R.string.quick_controls_title)
- `when`(controlsComponent.getTileTitleId()).thenReturn(R.drawable.controls_icon)
+ `when`(controlsComponent.getTileImageId()).thenReturn(R.drawable.controls_icon)
}
@Test
@@ -378,6 +389,28 @@
assertThat(tile.tileLabel).isEqualTo(context.getText(controlsComponent.getTileTitleId()))
}
+ @Test
+ @DisableFlags(QSComposeFragment.FLAG_NAME)
+ fun tileIconEqualsResourceFromComponent_composeFlagDisabled() {
+ tile.refreshState()
+ testableLooper.processAllMessages()
+ assertThat(tile.state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.controls_icon))
+ }
+
+ @Test
+ @EnableFlags(QSComposeFragment.FLAG_NAME)
+ fun tileIconEqualsResourceFromComponent_composeFlagEnable() {
+ tile.refreshState()
+ testableLooper.processAllMessages()
+ assertThat(tile.state.icon)
+ .isEqualTo(
+ QSTileImpl.DrawableIconWithRes(
+ mContext.getDrawable(R.drawable.controls_icon),
+ R.drawable.controls_icon,
+ )
+ )
+ }
+
private fun createTile(): DeviceControlsTile {
return DeviceControlsTile(
qsHost,
@@ -396,6 +429,14 @@
testableLooper.processAllMessages()
}
}
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
+ }
}
private const val CONTROLS_ACTIVITY_CLASS_NAME = "com.android.systemui.controls.ui.ControlsActivity"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
index f90463e..6a15a5b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
@@ -1,12 +1,11 @@
package com.android.systemui.qs.tiles
-import android.content.Context
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
@@ -14,8 +13,12 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.FlashlightController
import com.google.common.truth.Truth
import org.junit.After
@@ -25,13 +28,17 @@
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
-class FlashlightTileTest : SysuiTestCase() {
+class FlashlightTileTest(flags: FlagsParameterization) : SysuiTestCase() {
- @Mock private lateinit var mockContext: Context
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
@Mock private lateinit var qsLogger: QSLogger
@@ -56,7 +63,7 @@
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
- Mockito.`when`(qsHost.context).thenReturn(mockContext)
+ Mockito.`when`(qsHost.context).thenReturn(mContext)
tile =
FlashlightTile(
@@ -69,7 +76,7 @@
statusBarStateController,
activityStarter,
qsLogger,
- flashlightController
+ flashlightController,
)
}
@@ -87,8 +94,7 @@
tile.handleUpdateState(state, /* arg= */ null)
- Truth.assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_flashlight_icon_on))
+ Truth.assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_flashlight_icon_on))
}
@Test
@@ -100,7 +106,7 @@
tile.handleUpdateState(state, /* arg= */ null)
Truth.assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_flashlight_icon_off))
+ .isEqualTo(createExpectedIcon(R.drawable.qs_flashlight_icon_off))
}
@Test
@@ -111,6 +117,22 @@
tile.handleUpdateState(state, /* arg= */ null)
Truth.assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_flashlight_icon_off))
+ .isEqualTo(createExpectedIcon(R.drawable.qs_flashlight_icon_off))
+ }
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
index f33de4d..eeccbdf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
@@ -17,10 +17,11 @@
package com.android.systemui.qs.tiles
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.service.quicksettings.Tile
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.systemui.SysuiTestCase
@@ -29,6 +30,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tiles.dialog.InternetDialogManager
import com.android.systemui.qs.tiles.dialog.WifiStateWorker
@@ -62,12 +64,18 @@
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidJUnit4::class)
-class InternetTileNewImplTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class InternetTileNewImplTest(flags: FlagsParameterization) : SysuiTestCase() {
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
lateinit var underTest: InternetTileNewImpl
private val testDispatcher = StandardTestDispatcher()
@@ -252,5 +260,11 @@
const val WIFI_SSID = "test ssid"
val ACTIVE_WIFI =
WifiNetworkModel.Active.of(isValidated = true, level = 4, ssid = WIFI_SSID)
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java
index b5ec0a0..d7b183e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java
@@ -16,6 +16,10 @@
package com.android.systemui.qs.tiles;
+import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
+
+import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
@@ -25,19 +29,21 @@
import static org.mockito.Mockito.when;
import android.os.Handler;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.service.quicksettings.Tile;
import android.testing.TestableLooper;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.flags.QsInCompose;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.qs.tiles.dialog.InternetDialogManager;
@@ -55,11 +61,21 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidJUnit4.class)
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class InternetTileTest extends SysuiTestCase {
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
+ }
+
@Mock
private QSHost mHost;
@Mock
@@ -76,6 +92,11 @@
private TestableLooper mTestableLooper;
private InternetTile mTile;
+ public InternetTileTest(FlagsParameterization flags) {
+ super();
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -172,7 +193,7 @@
mTile.mSignalCallback.setIsAirplaneMode(state);
mTestableLooper.processAllMessages();
assertThat(mTile.getState().icon).isEqualTo(
- QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable));
+ createExpectedIcon(R.drawable.ic_qs_no_internet_unavailable));
}
@Test
@@ -194,4 +215,12 @@
verify(mWifiStateWorker, times(1)).setWifiEnabled(eq(true));
}
+
+ private QSTile.Icon createExpectedIcon(int resId) {
+ if (QsInCompose.isEnabled()) {
+ return new QSTileImpl.DrawableIconWithRes(mContext.getDrawable(resId), resId);
+ } else {
+ return QSTileImpl.ResourceIcon.get(resId);
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/LocationTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
index 0a1455f..a581b57 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
@@ -16,13 +16,12 @@
package com.android.systemui.qs.tiles
-import android.content.Context
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
@@ -30,9 +29,13 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.policy.LocationController
import com.android.systemui.util.mockito.argumentCaptor
@@ -46,33 +49,28 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
-class LocationTileTest : SysuiTestCase() {
+class LocationTileTest(flags: FlagsParameterization) : SysuiTestCase() {
- @Mock
- private lateinit var mockContext: Context
- @Mock
- private lateinit var qsLogger: QSLogger
- @Mock
- private lateinit var qsHost: QSHost
- @Mock
- private lateinit var metricsLogger: MetricsLogger
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ @Mock private lateinit var qsLogger: QSLogger
+ @Mock private lateinit var qsHost: QSHost
+ @Mock private lateinit var metricsLogger: MetricsLogger
private val falsingManager = FalsingManagerFake()
- @Mock
- private lateinit var statusBarStateController: StatusBarStateController
- @Mock
- private lateinit var activityStarter: ActivityStarter
- @Mock
- private lateinit var locationController: LocationController
- @Mock
- private lateinit var keyguardStateController: KeyguardStateController
- @Mock
- private lateinit var panelInteractor: PanelInteractor
- @Mock
- private lateinit var uiEventLogger: QsEventLogger
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var locationController: LocationController
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var panelInteractor: PanelInteractor
+ @Mock private lateinit var uiEventLogger: QsEventLogger
private lateinit var testableLooper: TestableLooper
private lateinit var tile: LocationTile
@@ -81,22 +79,23 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
- `when`(qsHost.context).thenReturn(mockContext)
+ `when`(qsHost.context).thenReturn(mContext)
- tile = LocationTile(
- qsHost,
- uiEventLogger,
- testableLooper.looper,
- Handler(testableLooper.looper),
- falsingManager,
- metricsLogger,
- statusBarStateController,
- activityStarter,
- qsLogger,
- locationController,
- keyguardStateController,
- panelInteractor,
- )
+ tile =
+ LocationTile(
+ qsHost,
+ uiEventLogger,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ falsingManager,
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ locationController,
+ keyguardStateController,
+ panelInteractor,
+ )
}
@After
@@ -112,8 +111,7 @@
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_location_icon_off))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_location_icon_off))
}
@Test
@@ -123,8 +121,7 @@
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_location_icon_on))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_location_icon_on))
}
@Test
@@ -140,4 +137,20 @@
verify(panelInteractor).openPanels()
}
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
index dbdf3a4..a39692d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
@@ -17,13 +17,13 @@
package com.android.systemui.qs.tiles
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.provider.Settings
import android.safetycenter.SafetyCenterManager
import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
@@ -31,8 +31,12 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.google.common.truth.Truth.assertThat
@@ -41,49 +45,52 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
-class MicrophoneToggleTileTest : SysuiTestCase() {
+class MicrophoneToggleTileTest(flags: FlagsParameterization) : SysuiTestCase() {
companion object {
/* isBlocked */
const val MICROPHONE_TOGGLE_ENABLED: Boolean = false
const val MICROPHONE_TOGGLE_DISABLED: Boolean = true
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
}
- @Mock
- private lateinit var host: QSHost
- @Mock
- private lateinit var metricsLogger: MetricsLogger
- @Mock
- private lateinit var statusBarStateController: StatusBarStateController
- @Mock
- private lateinit var activityStarter: ActivityStarter
- @Mock
- private lateinit var qsLogger: QSLogger
- @Mock
- private lateinit var privacyController: IndividualSensorPrivacyController
- @Mock
- private lateinit var keyguardStateController: KeyguardStateController
- @Mock
- private lateinit var uiEventLogger: QsEventLogger
- @Mock
- private lateinit var safetyCenterManager: SafetyCenterManager
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ @Mock private lateinit var host: QSHost
+ @Mock private lateinit var metricsLogger: MetricsLogger
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var qsLogger: QSLogger
+ @Mock private lateinit var privacyController: IndividualSensorPrivacyController
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var uiEventLogger: QsEventLogger
+ @Mock private lateinit var safetyCenterManager: SafetyCenterManager
private lateinit var testableLooper: TestableLooper
private lateinit var tile: MicrophoneToggleTile
-
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
whenever(host.context).thenReturn(mContext)
- tile = MicrophoneToggleTile(
+ tile =
+ MicrophoneToggleTile(
host,
uiEventLogger,
testableLooper.looper,
@@ -95,7 +102,8 @@
qsLogger,
privacyController,
keyguardStateController,
- safetyCenterManager)
+ safetyCenterManager,
+ )
}
@After
@@ -110,7 +118,7 @@
tile.handleUpdateState(state, MICROPHONE_TOGGLE_ENABLED)
- assertThat(state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_mic_access_on))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_mic_access_on))
}
@Test
@@ -119,13 +127,14 @@
tile.handleUpdateState(state, MICROPHONE_TOGGLE_DISABLED)
- assertThat(state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_mic_access_off))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_mic_access_off))
}
@Test
fun testLongClickIntent_safetyCenterEnabled() {
whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(true)
- val micTile = MicrophoneToggleTile(
+ val micTile =
+ MicrophoneToggleTile(
host,
uiEventLogger,
testableLooper.looper,
@@ -137,7 +146,8 @@
qsLogger,
privacyController,
keyguardStateController,
- safetyCenterManager)
+ safetyCenterManager,
+ )
assertThat(micTile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_CONTROLS)
micTile.destroy()
testableLooper.processAllMessages()
@@ -146,7 +156,8 @@
@Test
fun testLongClickIntent_safetyCenterDisabled() {
whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(false)
- val micTile = MicrophoneToggleTile(
+ val micTile =
+ MicrophoneToggleTile(
host,
uiEventLogger,
testableLooper.looper,
@@ -158,9 +169,18 @@
qsLogger,
privacyController,
keyguardStateController,
- safetyCenterManager)
+ safetyCenterManager,
+ )
assertThat(micTile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_SETTINGS)
micTile.destroy()
testableLooper.processAllMessages()
}
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
index 848c8db..9173ac9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
@@ -94,11 +94,7 @@
private val zenModeRepository = kosmos.zenModeRepository
private val tileDataInteractor =
ModesTileDataInteractor(context, kosmos.zenModeInteractor, testDispatcher)
- private val mapper =
- ModesTileMapper(
- context.resources,
- context.theme,
- )
+ private val mapper = ModesTileMapper(context.resources, context.theme)
private lateinit var userActionInteractor: ModesTileUserActionInteractor
private lateinit var secureSettings: SecureSettings
@@ -127,10 +123,7 @@
)
userActionInteractor =
- ModesTileUserActionInteractor(
- inputHandler,
- dialogDelegate,
- )
+ ModesTileUserActionInteractor(inputHandler, dialogDelegate, kosmos.zenModeInteractor)
underTest =
ModesTile(
@@ -185,7 +178,7 @@
ModesTileModel(
isActivated = true,
activeModes = listOf("One", "Two"),
- icon = TestStubDrawable().asIcon()
+ icon = TestStubDrawable().asIcon(),
)
underTest.handleUpdateState(tileState, model)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
index f1c5895..a193cbc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
@@ -19,11 +19,11 @@
import android.hardware.display.ColorDisplayManager
import android.hardware.display.NightDisplayListener
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.dagger.NightDisplayListenerModule
@@ -32,8 +32,12 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.LocationController
import com.google.common.truth.Truth
import org.junit.After
@@ -42,13 +46,20 @@
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.anyInt
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
-class NightDisplayTileTest : SysuiTestCase() {
+class NightDisplayTileTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
@Mock private lateinit var mHost: QSHost
@Mock private lateinit var mMetricsLogger: MetricsLogger
@@ -72,8 +83,6 @@
private lateinit var mTestableLooper: TestableLooper
private lateinit var mTile: NightDisplayTile
-
-
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -97,7 +106,7 @@
mQsLogger,
mLocationController,
mColorDisplayManager,
- mNightDisplayListenerBuilder
+ mNightDisplayListenerBuilder,
)
}
@@ -115,7 +124,7 @@
mTile.handleUpdateState(state, /* arg= */ null)
Truth.assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_nightlight_icon_off))
+ .isEqualTo(createExpectedIcon(R.drawable.qs_nightlight_icon_off))
}
@Test
@@ -125,7 +134,22 @@
mTile.handleUpdateState(state, /* arg= */ null)
- Truth.assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_nightlight_icon_on))
+ Truth.assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_nightlight_icon_on))
+ }
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
index f8f82f2..682daea 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
@@ -38,6 +38,7 @@
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.flags.QsInCompose;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.res.R;
@@ -112,7 +113,7 @@
assertEquals(state.label, mContext.getString(R.string.qr_code_scanner_title));
assertEquals(state.contentDescription, mContext.getString(R.string.qr_code_scanner_title));
- assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.ic_qr_code_scanner));
+ assertEquals(state.icon, createExpectedIcon(R.drawable.ic_qr_code_scanner));
}
@Test
@@ -133,4 +134,12 @@
assertEquals(state.state, Tile.STATE_INACTIVE);
assertNull(state.secondaryLabel);
}
+
+ private QSTile.Icon createExpectedIcon(int resId) {
+ if (QsInCompose.isEnabled()) {
+ return new QSTileImpl.DrawableIconWithRes(mContext.getDrawable(resId), resId);
+ } else {
+ return QSTileImpl.ResourceIcon.get(resId);
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index 4068d9f..3395167 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -68,6 +68,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.flags.QsInCompose;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.res.R;
@@ -294,7 +295,7 @@
public void testHandleUpdateState_updateLabelAndIcon_noIconFromApi() {
when(mQuickAccessWalletClient.getTileIcon()).thenReturn(null);
QSTile.State state = new QSTile.State();
- QSTile.Icon icon = QSTileImpl.ResourceIcon.get(R.drawable.ic_wallet_lockscreen);
+ QSTile.Icon icon = createExpectedIcon(R.drawable.ic_wallet_lockscreen);
mTile.handleUpdateState(state, null);
@@ -574,5 +575,13 @@
CARD_ID, INVALID_CARD_IMAGE, CARD_DESCRIPTION, pendingIntent).build();
}
+ private QSTile.Icon createExpectedIcon(int resId) {
+ if (QsInCompose.isEnabled()) {
+ return new QSTileImpl.DrawableIconWithRes(mContext.getDrawable(resId), resId);
+ } else {
+ return QSTileImpl.ResourceIcon.get(resId);
+ }
+ }
+
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
index 1aff45b..c244984 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
@@ -16,6 +16,10 @@
package com.android.systemui.qs.tiles;
+import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
+
+import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
+
import static junit.framework.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
@@ -28,10 +32,10 @@
import android.os.Handler;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.service.quicksettings.Tile;
import android.testing.TestableLooper;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
@@ -46,6 +50,7 @@
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.ReduceBrightColorsController;
+import com.android.systemui.qs.flags.QsInCompose;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.res.R.drawable;
@@ -58,10 +63,21 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidJUnit4.class)
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class ReduceBrightColorsTileTest extends SysuiTestCase {
+
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
+ }
+
@Mock
private QSHost mHost;
@Mock
@@ -84,6 +100,11 @@
private TestableLooper mTestableLooper;
private ReduceBrightColorsTile mTile;
+ public ReduceBrightColorsTileTest(FlagsParameterization flags) {
+ super();
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -234,7 +255,7 @@
mTile.handleUpdateState(state, /* arg= */ null);
- assertEquals(state.icon, QSTileImpl.ResourceIcon.get(drawable.qs_extra_dim_icon_on));
+ assertEquals(state.icon, createExpectedIcon(drawable.qs_extra_dim_icon_on));
}
@Test
@@ -245,7 +266,15 @@
mTile.handleUpdateState(state, /* arg= */ null);
- assertEquals(state.icon, QSTileImpl.ResourceIcon.get(drawable.qs_extra_dim_icon_off));
+ assertEquals(state.icon, createExpectedIcon(drawable.qs_extra_dim_icon_off));
+ }
+
+ private QSTile.Icon createExpectedIcon(int resId) {
+ if (QsInCompose.isEnabled()) {
+ return new QSTileImpl.DrawableIconWithRes(mContext.getDrawable(resId), resId);
+ } else {
+ return QSTileImpl.ResourceIcon.get(resId);
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
index 4193063..fee358a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
@@ -17,6 +17,9 @@
package com.android.systemui.qs.tiles;
import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
+
+import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
import static junit.framework.TestCase.assertEquals;
@@ -27,10 +30,10 @@
import android.content.pm.PackageManager;
import android.hardware.SensorPrivacyManager;
import android.os.Handler;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.testing.TestableLooper;
import android.testing.TestableResources;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
@@ -41,6 +44,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.flags.QsInCompose;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.res.R;
@@ -58,7 +62,12 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidJUnit4.class)
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class RotationLockTileTest extends SysuiTestCase {
@@ -69,6 +78,11 @@
"1:2"
};
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
+ }
+
@Mock
private PackageManager mPackageManager;
@Mock
@@ -97,6 +111,11 @@
private RotationLockTile mLockTile;
private TestableResources mTestableResources;
+ public RotationLockTileTest(FlagsParameterization flags) {
+ super();
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -247,7 +266,7 @@
mLockTile.handleUpdateState(state, /* arg= */ null);
- assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_auto_rotate_icon_off));
+ assertEquals(state.icon, createExpectedIcon(R.drawable.qs_auto_rotate_icon_off));
}
@Test
@@ -257,7 +276,7 @@
mLockTile.handleUpdateState(state, /* arg= */ null);
- assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_auto_rotate_icon_on));
+ assertEquals(state.icon, createExpectedIcon(R.drawable.qs_auto_rotate_icon_on));
}
@@ -281,4 +300,12 @@
private void disableCameraBasedRotation() {
when(mRotationPolicyWrapper.isCameraRotationEnabled()).thenReturn(false);
}
+
+ private QSTile.Icon createExpectedIcon(int resId) {
+ if (QsInCompose.isEnabled()) {
+ return new QSTileImpl.DrawableIconWithRes(mContext.getDrawable(resId), resId);
+ } else {
+ return QSTileImpl.ResourceIcon.get(resId);
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index 6ebe830..fc1d73b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -16,6 +16,10 @@
package com.android.systemui.qs.tiles;
+import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
+
+import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
@@ -31,10 +35,10 @@
import android.app.Dialog;
import android.media.projection.StopReason;
import android.os.Handler;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.service.quicksettings.Tile;
import android.testing.TestableLooper;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
@@ -48,6 +52,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.flags.QsInCompose;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -65,11 +70,21 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidJUnit4.class)
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class ScreenRecordTileTest extends SysuiTestCase {
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
+ }
+
@Mock
private RecordingController mController;
@Mock
@@ -104,6 +119,11 @@
private TestableLooper mTestableLooper;
private ScreenRecordTile mTile;
+ public ScreenRecordTileTest(FlagsParameterization flags) {
+ super();
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -268,7 +288,7 @@
mTile.handleUpdateState(state, /* arg= */ null);
- assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_screen_record_icon_on));
+ assertEquals(state.icon, createExpectedIcon(R.drawable.qs_screen_record_icon_on));
}
@Test
@@ -279,7 +299,7 @@
mTile.handleUpdateState(state, /* arg= */ null);
- assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_screen_record_icon_on));
+ assertEquals(state.icon, createExpectedIcon(R.drawable.qs_screen_record_icon_on));
}
@Test
@@ -290,7 +310,7 @@
mTile.handleUpdateState(state, /* arg= */ null);
- assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_screen_record_icon_off));
+ assertEquals(state.icon, createExpectedIcon(R.drawable.qs_screen_record_icon_off));
}
@Test
@@ -316,4 +336,12 @@
.notifyPermissionRequestDisplayed(mContext.getUserId());
}
+ private QSTile.Icon createExpectedIcon(int resId) {
+ if (QsInCompose.isEnabled()) {
+ return new QSTileImpl.DrawableIconWithRes(mContext.getDrawable(resId), resId);
+ } else {
+ return QSTileImpl.ResourceIcon.get(resId);
+ }
+ }
+
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
index 8324a73..3246e64 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
@@ -17,15 +17,13 @@
package com.android.systemui.qs.tiles
import android.app.UiModeManager
-import android.content.Context
import android.content.res.Configuration
-import android.content.res.Resources
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
@@ -33,8 +31,12 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.LocationController
@@ -46,15 +48,19 @@
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
-class UiModeNightTileTest : SysuiTestCase() {
+class UiModeNightTileTest(flags: FlagsParameterization) : SysuiTestCase() {
- @Mock private lateinit var mockContext: Context
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
@Mock private lateinit var uiModeManager: UiModeManager
- @Mock private lateinit var resources: Resources
@Mock private lateinit var qsLogger: QSLogger
@Mock private lateinit var qsHost: QSHost
@Mock private lateinit var metricsLogger: MetricsLogger
@@ -68,19 +74,19 @@
private val falsingManager = FalsingManagerFake()
private lateinit var testableLooper: TestableLooper
private lateinit var tile: UiModeNightTile
- private lateinit var configuration: Configuration
+ private val configuration = Configuration()
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ val initialConfiguration = mContext.resources.configuration
+ onTeardown { mContext.resources.configuration.updateFrom(initialConfiguration) }
+
testableLooper = TestableLooper.get(this)
- configuration = Configuration()
mContext.addMockSystemService(UiModeManager::class.java, uiModeManager)
- `when`(qsHost.context).thenReturn(mockContext)
+ `when`(qsHost.context).thenReturn(mContext)
`when`(qsHost.userContext).thenReturn(mContext)
- `when`(mockContext.resources).thenReturn(resources)
- `when`(resources.configuration).thenReturn(configuration)
tile =
UiModeNightTile(
@@ -95,7 +101,7 @@
qsLogger,
configurationController,
batteryController,
- locationController
+ locationController,
)
}
@@ -112,28 +118,45 @@
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_light_dark_theme_icon_on))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_light_dark_theme_icon_on))
}
@Test
- fun testIcon_whenNightModeOn_isOffState() {
+ fun testIcon_whenNightModeOff_isOffState() {
val state = QSTile.BooleanState()
setNightModeOff()
tile.handleUpdateState(state, /* arg= */ null)
assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_light_dark_theme_icon_off))
+ .isEqualTo(createExpectedIcon(R.drawable.qs_light_dark_theme_icon_off))
}
private fun setNightModeOn() {
`when`(uiModeManager.nightMode).thenReturn(UiModeManager.MODE_NIGHT_YES)
configuration.uiMode = Configuration.UI_MODE_NIGHT_YES
+ mContext.resources.configuration.updateFrom(configuration)
}
private fun setNightModeOff() {
`when`(uiModeManager.nightMode).thenReturn(UiModeManager.MODE_NIGHT_NO)
configuration.uiMode = Configuration.UI_MODE_NIGHT_NO
+ mContext.resources.configuration.updateFrom(configuration)
+ }
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
index 52c476e..e4a9888 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
@@ -33,6 +33,7 @@
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -136,4 +137,13 @@
verify(wifiStateWorker, times(1)).isWifiEnabled = eq(true)
}
+
+ @Test
+ fun detailsViewModel() =
+ kosmos.testScope.runTest {
+ assertThat(underTest.detailsViewModel.getTitle())
+ .isEqualTo("Internet")
+ assertThat(underTest.detailsViewModel.getSubTitle())
+ .isEqualTo("Tab a network to connect")
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
index cd58127..88b0046 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.qs.tiles.impl.modes.domain.interactor
import android.graphics.drawable.TestStubDrawable
@@ -21,16 +23,23 @@
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.settingslib.notification.modes.TestModeBuilder
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.asIcon
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
+import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
+import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.statusbar.policy.ui.dialog.mockModesDialogDelegate
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -43,17 +52,17 @@
@EnableFlags(android.app.Flags.FLAG_MODES_UI)
class ModesTileUserActionInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
private val inputHandler = kosmos.qsTileIntentUserInputHandler
private val mockDialogDelegate = kosmos.mockModesDialogDelegate
+ private val zenModeRepository = kosmos.zenModeRepository
+ private val zenModeInteractor = kosmos.zenModeInteractor
private val underTest =
- ModesTileUserActionInteractor(
- inputHandler,
- mockDialogDelegate,
- )
+ ModesTileUserActionInteractor(inputHandler, mockDialogDelegate, zenModeInteractor)
@Test
- fun handleClick_active() = runTest {
+ fun handleClick_active_showsDialog() = runTest {
val expandable = mock<Expandable>()
underTest.handleInput(
QSTileInputTestKtx.click(data = modelOf(true, listOf("DND")), expandable = expandable)
@@ -63,7 +72,7 @@
}
@Test
- fun handleClick_inactive() = runTest {
+ fun handleClick_inactive_showsDialog() = runTest {
val expandable = mock<Expandable>()
underTest.handleInput(
QSTileInputTestKtx.click(data = modelOf(false, emptyList()), expandable = expandable)
@@ -73,7 +82,63 @@
}
@Test
- fun handleLongClick_active() = runTest {
+ @EnableFlags(Flags.FLAG_QS_UI_REFACTOR_COMPOSE_FRAGMENT)
+ fun handleToggleClick_multipleModesActive_deactivatesAll() =
+ testScope.runTest {
+ val activeModes by collectLastValue(zenModeInteractor.activeModes)
+
+ zenModeRepository.addModes(
+ listOf(
+ TestModeBuilder.MANUAL_DND_ACTIVE,
+ TestModeBuilder().setName("Mode 1").setActive(true).build(),
+ TestModeBuilder().setName("Mode 2").setActive(true).build(),
+ )
+ )
+ assertThat(activeModes?.modeNames?.count()).isEqualTo(3)
+
+ underTest.handleInput(
+ QSTileInputTestKtx.toggleClick(
+ data = modelOf(true, listOf("DND", "Mode 1", "Mode 2"))
+ )
+ )
+
+ assertThat(activeModes?.isAnyActive()).isFalse()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_QS_UI_REFACTOR_COMPOSE_FRAGMENT)
+ fun handleToggleClick_dndActive_deactivatesDnd() =
+ testScope.runTest {
+ val dndMode by collectLastValue(zenModeInteractor.dndMode)
+
+ zenModeRepository.addMode(TestModeBuilder.MANUAL_DND_ACTIVE)
+ assertThat(dndMode?.isActive).isTrue()
+
+ underTest.handleInput(
+ QSTileInputTestKtx.toggleClick(data = modelOf(true, listOf("DND")))
+ )
+
+ assertThat(dndMode?.isActive).isFalse()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_QS_UI_REFACTOR_COMPOSE_FRAGMENT)
+ fun handleToggleClick_dndInactive_activatesDnd() =
+ testScope.runTest {
+ val dndMode by collectLastValue(zenModeInteractor.dndMode)
+
+ zenModeRepository.addMode(TestModeBuilder.MANUAL_DND_INACTIVE)
+ assertThat(dndMode?.isActive).isFalse()
+
+ underTest.handleInput(
+ QSTileInputTestKtx.toggleClick(data = modelOf(false, emptyList()))
+ )
+
+ assertThat(dndMode?.isActive).isTrue()
+ }
+
+ @Test
+ fun handleLongClick_active_opensSettings() = runTest {
underTest.handleInput(QSTileInputTestKtx.longClick(modelOf(true, listOf("DND"))))
QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
@@ -82,7 +147,7 @@
}
@Test
- fun handleLongClick_inactive() = runTest {
+ fun handleLongClick_inactive_opensSettings() = runTest {
underTest.handleInput(QSTileInputTestKtx.longClick(modelOf(false, emptyList())))
QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
index 954215ee..2edb9c6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
@@ -173,6 +173,21 @@
.isEqualTo(FakeQSTileDataInteractor.AvailabilityRequest(USER))
}
+ @Test
+ fun tileDetails() =
+ testScope.runTest {
+ assertThat(tileUserActionInteractor.detailsViewModel).isNotNull()
+ assertThat(tileUserActionInteractor.detailsViewModel?.getTitle())
+ .isEqualTo("FakeQSTileUserActionInteractor")
+ assertThat(underTest.detailsViewModel).isNotNull()
+ assertThat(underTest.detailsViewModel?.getTitle())
+ .isEqualTo("FakeQSTileUserActionInteractor")
+
+ tileUserActionInteractor.detailsViewModel = null
+ assertThat(tileUserActionInteractor.detailsViewModel).isNull()
+ assertThat(underTest.detailsViewModel).isNull()
+ }
+
private fun createViewModel(
scope: TestScope,
config: QSTileConfig = tileConfig,
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 b5f005c..e56b965 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -47,6 +47,7 @@
import com.android.systemui.keyguard.ui.viewmodel.lockscreenUserActionsViewModel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.currentValue
import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
@@ -77,6 +78,7 @@
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -399,9 +401,8 @@
* Note that this doesn't assert what the current scene is in the UI.
*/
private fun Kosmos.assertCurrentScene(expected: SceneKey) {
- testScope.runCurrent()
assertWithMessage("Current scene mismatch!")
- .that(sceneContainerViewModel.currentScene.value)
+ .that(currentValue(sceneContainerViewModel.currentScene))
.isEqualTo(expected)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt
index bb2e941..fc915ca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt
@@ -104,7 +104,7 @@
runCurrent()
assertThat(userActions?.get(swipeDownFromTopWithTwoFingers()))
- .isEqualTo(UserActionResult(Scenes.QuickSettings, isIrreversible = true))
+ .isEqualTo(UserActionResult(Scenes.QuickSettings))
}
@Test
@@ -116,7 +116,7 @@
runCurrent()
assertThat(userActions?.get(swipeDownFromTopWithTwoFingers()))
- .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true))
+ .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade))
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
index 612d646..53a083f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
@@ -17,13 +17,13 @@
package com.android.systemui.screenshot
import android.content.Intent
-import androidx.test.ext.junit.runners.AndroidJUnit4
import android.os.Process.myUserHandle
import android.platform.test.annotations.EnableFlags
import android.testing.TestableContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.screenshot.proxy.SystemUiProxy
+import com.android.systemui.screenshot.proxy.ScreenshotProxy
import com.android.systemui.settings.DisplayTracker
import com.android.systemui.shared.system.ActivityManagerWrapper
import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -45,7 +45,7 @@
private val testableContext = TestableContext(mContext)
private val activityManagerWrapper = mock<ActivityManagerWrapper>()
- private val systemUiProxy = mock<SystemUiProxy>()
+ private val screenshotProxy = mock<ScreenshotProxy>()
private val displayTracker = mock<DisplayTracker>()
@@ -55,7 +55,7 @@
activityManagerWrapper,
testScope,
mainDispatcher,
- systemUiProxy,
+ screenshotProxy,
displayTracker,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt
index 7842d75..f9b4488 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt
@@ -111,6 +111,8 @@
override fun getCurrentCardTopPadding(): Int {
return 0
}
+
+ override fun setHorizontalPaddings(horizontalPadding: Int) {}
}
@Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
index c83c82d..70ba00e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
@@ -127,6 +127,8 @@
override fun getCurrentCardTopPadding(): Int {
return 0
}
+
+ override fun setHorizontalPaddings(horizontalPadding: Int) {}
}
@Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
index 3ebf9f7..a62d9d5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -34,6 +34,7 @@
import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
@@ -163,6 +164,28 @@
}
@Test
+ @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
+ fun chip_positiveStartTime_notifIconAndConnectedDisplaysFlagOn_iconIsNotifIcon() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ val notifKey = "testNotifKey"
+ repo.setOngoingCallState(
+ inCallModel(startTimeMs = 1000, notificationIcon = null, notificationKey = notifKey)
+ )
+
+ assertThat((latest as OngoingActivityChipModel.Shown).icon)
+ .isInstanceOf(
+ OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon::class.java
+ )
+ val actualNotifKey =
+ (((latest as OngoingActivityChipModel.Shown).icon)
+ as OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon)
+ .notificationKey
+ assertThat(actualNotifKey).isEqualTo(notifKey)
+ }
+
+ @Test
@DisableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
fun chip_zeroStartTime_notifIconFlagOff_iconIsPhone() =
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
index e96dd16..f06bab7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.chips.notification.domain.interactor
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -25,6 +26,8 @@
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.chips.notification.domain.model.NotificationChipModel
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -111,6 +114,27 @@
}
@Test
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun notificationChip_cdEnabled_missingStatusBarIconChipView_inConstructor_emitsNotNull() =
+ kosmos.runTest {
+ val underTest =
+ factory.create(
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = null,
+ whenTime = 123L,
+ )
+ )
+
+ val latest by collectLastValue(underTest.notificationChip)
+
+ assertThat(latest)
+ .isEqualTo(
+ NotificationChipModel("notif1", statusBarChipIconView = null, whenTime = 123L)
+ )
+ }
+
+ @Test
fun notificationChip_missingStatusBarIconChipView_inSet_emitsNull() =
kosmos.runTest {
val startingNotif = activeNotificationModel(key = "notif1", statusBarChipIcon = mock())
@@ -126,6 +150,29 @@
}
@Test
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun notificationChip_missingStatusBarIconChipView_inSet_cdEnabled_emitsNotNull() =
+ kosmos.runTest {
+ val startingNotif = activeNotificationModel(key = "notif1", statusBarChipIcon = mock())
+ val underTest = factory.create(startingNotif)
+ val latest by collectLastValue(underTest.notificationChip)
+ assertThat(latest).isNotNull()
+
+ underTest.setNotification(
+ activeNotificationModel(key = "notif1", statusBarChipIcon = null, whenTime = 123L)
+ )
+
+ assertThat(latest)
+ .isEqualTo(
+ NotificationChipModel(
+ key = "notif1",
+ statusBarChipIconView = null,
+ whenTime = 123L,
+ )
+ )
+ }
+
+ @Test
fun notificationChip_appIsVisibleOnCreation_emitsNull() =
kosmos.runTest {
activityManagerRepository.fake.startingIsAppVisibleValue = true
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
index 11831ca..8a4ddce 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
@@ -29,6 +29,7 @@
import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
@@ -107,6 +108,30 @@
}
@Test
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun chips_onePromotedNotif_connectedDisplaysFlagEnabled_statusBarIconMatches() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val notifKey = "notif"
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = notifKey,
+ statusBarChipIcon = null,
+ promotedContent = PromotedNotificationContentModel.Builder(notifKey).build(),
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ val chip = latest!![0]
+ assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+ assertThat(chip.icon)
+ .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon(notifKey))
+ }
+
+ @Test
fun chips_onlyForPromotedNotifs() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -139,6 +164,41 @@
}
@Test
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun chips_connectedDisplaysFlagEnabled_onlyForPromotedNotifs() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val firstKey = "notif1"
+ val secondKey = "notif2"
+ val thirdKey = "notif3"
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = firstKey,
+ statusBarChipIcon = null,
+ promotedContent = PromotedNotificationContentModel.Builder(firstKey).build(),
+ ),
+ activeNotificationModel(
+ key = secondKey,
+ statusBarChipIcon = null,
+ promotedContent =
+ PromotedNotificationContentModel.Builder(secondKey).build(),
+ ),
+ activeNotificationModel(
+ key = thirdKey,
+ statusBarChipIcon = null,
+ promotedContent = null,
+ ),
+ )
+ )
+
+ assertThat(latest).hasSize(2)
+ assertIsNotifKey(latest!![0], firstKey)
+ assertIsNotifKey(latest!![1], secondKey)
+ }
+
+ @Test
fun chips_clickingChipNotifiesInteractor() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -178,5 +238,12 @@
assertThat((latest as OngoingActivityChipModel.Shown).icon)
.isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(expectedIcon))
}
+
+ fun assertIsNotifKey(latest: OngoingActivityChipModel?, expectedKey: String) {
+ assertThat(latest)
+ .isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+ assertThat((latest as OngoingActivityChipModel.Shown).icon)
+ .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon(expectedKey))
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt
index 1b3f29a..dd1b369 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.core
+import android.internal.statusbar.FakeStatusBarService.Companion.SECONDARY_DISPLAY_ID
import android.internal.statusbar.fakeStatusBarService
import android.platform.test.annotations.EnableFlags
import android.view.WindowInsets
@@ -53,7 +54,7 @@
}
@Test
- fun start_barResultHasTransientStatusBar_transientStateIsTrue() {
+ fun start_defaultDisplay_barResultHasTransientStatusBar_transientStateIsTrue() {
fakeStatusBarService.transientBarTypes = WindowInsets.Type.statusBars()
initializer.start()
@@ -62,7 +63,7 @@
}
@Test
- fun start_barResultDoesNotHaveTransientStatusBar_transientStateIsFalse() {
+ fun start_defaultDisplay_barResultDoesNotHaveTransientStatusBar_transientStateIsFalse() {
fakeStatusBarService.transientBarTypes = WindowInsets.Type.navigationBars()
initializer.start()
@@ -71,6 +72,32 @@
}
@Test
+ fun start_secondaryDisplay_barResultHasTransientStatusBar_transientStateIsTrue() {
+ fakeStatusBarService.transientBarTypesSecondaryDisplay = WindowInsets.Type.statusBars()
+ fakeStatusBarService.transientBarTypes = WindowInsets.Type.navigationBars()
+
+ initializer.start()
+
+ assertThat(statusBarModeRepository.forDisplay(SECONDARY_DISPLAY_ID).isTransientShown.value)
+ .isTrue()
+ // Default display should be unaffected
+ assertThat(statusBarModeRepository.defaultDisplay.isTransientShown.value).isFalse()
+ }
+
+ @Test
+ fun start_secondaryDisplay_barResultDoesNotHaveTransientStatusBar_transientStateIsFalse() {
+ fakeStatusBarService.transientBarTypesSecondaryDisplay = WindowInsets.Type.navigationBars()
+ fakeStatusBarService.transientBarTypes = WindowInsets.Type.statusBars()
+
+ initializer.start()
+
+ assertThat(statusBarModeRepository.forDisplay(SECONDARY_DISPLAY_ID).isTransientShown.value)
+ .isFalse()
+ // Default display should be unaffected
+ assertThat(statusBarModeRepository.defaultDisplay.isTransientShown.value).isTrue()
+ }
+
+ @Test
fun start_callsOnSystemBarAttributesChanged_basedOnRegisterBarResult() {
initializer.start()
@@ -85,6 +112,17 @@
fakeStatusBarService.packageName,
fakeStatusBarService.letterboxDetails,
)
+ verify(commandQueueCallbacks)
+ .onSystemBarAttributesChanged(
+ SECONDARY_DISPLAY_ID,
+ fakeStatusBarService.appearanceSecondaryDisplay,
+ fakeStatusBarService.appearanceRegionsSecondaryDisplay,
+ fakeStatusBarService.navbarColorManagedByImeSecondaryDisplay,
+ fakeStatusBarService.behaviorSecondaryDisplay,
+ fakeStatusBarService.requestedVisibleTypesSecondaryDisplay,
+ fakeStatusBarService.packageNameSecondaryDisplay,
+ fakeStatusBarService.letterboxDetailsSecondaryDisplay,
+ )
}
@Test
@@ -105,6 +143,14 @@
fakeStatusBarService.imeBackDisposition,
fakeStatusBarService.showImeSwitcher,
)
+
+ verify(commandQueueCallbacks)
+ .setImeWindowStatus(
+ SECONDARY_DISPLAY_ID,
+ fakeStatusBarService.imeWindowVisSecondaryDisplay,
+ fakeStatusBarService.imeBackDispositionSecondaryDisplay,
+ fakeStatusBarService.showImeSwitcherSecondaryDisplay,
+ )
}
@Test
@@ -117,6 +163,11 @@
.isEqualTo(fakeStatusBarService.disabledFlags1)
assertThat(commandQueue.disableFlags2ForDisplay(context.displayId))
.isEqualTo(fakeStatusBarService.disabledFlags2)
+
+ assertThat(commandQueue.disableFlags1ForDisplay(SECONDARY_DISPLAY_ID))
+ .isEqualTo(fakeStatusBarService.disabledFlags1SecondaryDisplay)
+ assertThat(commandQueue.disableFlags2ForDisplay(SECONDARY_DISPLAY_ID))
+ .isEqualTo(fakeStatusBarService.disabledFlags2SecondaryDisplay)
}
@Test
@@ -125,5 +176,7 @@
assertThat(commandQueue.disableFlags1ForDisplay(context.displayId)).isNull()
assertThat(commandQueue.disableFlags2ForDisplay(context.displayId)).isNull()
+ assertThat(commandQueue.disableFlags1ForDisplay(SECONDARY_DISPLAY_ID)).isNull()
+ assertThat(commandQueue.disableFlags2ForDisplay(SECONDARY_DISPLAY_ID)).isNull()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index fe287ef..611ae3d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -1109,6 +1109,9 @@
override fun getCurrentCardTopPadding(): Int {
return 0
}
+
+ override fun setHorizontalPaddings(horizontalPadding: Int) {
+ }
})
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
index 689fc7c..9e7befd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.statusbar.notification.collection.coordinator
import android.app.Notification
@@ -22,671 +24,809 @@
import android.os.UserHandle
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
import android.service.notification.StatusBarNotification
-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.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.keyguardUpdateMonitor
import com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING
import com.android.systemui.SysuiTestCase
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
-import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.flags.andSceneContainer
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.statusbar.fakeStatusBarStateController
+import com.android.systemui.plugins.statusbar.statusBarStateController
import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.RankingBuilder
import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.DynamicPrivacyController
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
-import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
+import com.android.systemui.statusbar.notification.collection.notifPipeline
+import com.android.systemui.statusbar.notification.dynamicPrivacyController
+import com.android.systemui.statusbar.notification.mockDynamicPrivacyController
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
-import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.notificationLockscreenUserManager
import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController
+import com.android.systemui.statusbar.policy.mockSensitiveNotificationProtectionController
+import com.android.systemui.statusbar.policy.sensitiveNotificationProtectionController
import com.android.systemui.testKosmos
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-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.withArgCaptor
-import dagger.BindsInstance
-import dagger.Component
-import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
+import org.mockito.kotlin.any
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(AndroidJUnit4::class)
-class SensitiveContentCoordinatorTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class SensitiveContentCoordinatorTest(flags: FlagsParameterization) : SysuiTestCase() {
- val kosmos = testKosmos()
+ val kosmos =
+ testKosmos().apply {
+ // Override some Kosmos objects with mocks or fakes for easier testability
+ dynamicPrivacyController = mockDynamicPrivacyController
+ sensitiveNotificationProtectionController =
+ mockSensitiveNotificationProtectionController
+ statusBarStateController = fakeStatusBarStateController
+ }
- val dynamicPrivacyController: DynamicPrivacyController = mock()
- val lockscreenUserManager: NotificationLockscreenUserManager = mock()
- val pipeline: NotifPipeline = mock()
- val keyguardUpdateMonitor: KeyguardUpdateMonitor = mock()
- val statusBarStateController: StatusBarStateController = mock()
- val keyguardStateController: KeyguardStateController = mock()
- val mSelectedUserInteractor: SelectedUserInteractor = mock()
+ val testScope = kosmos.testScope
+
+ val dynamicPrivacyController: DynamicPrivacyController = kosmos.mockDynamicPrivacyController
+ val lockscreenUserManager: NotificationLockscreenUserManager =
+ kosmos.notificationLockscreenUserManager
+ val pipeline: NotifPipeline = kosmos.notifPipeline
+ val keyguardUpdateMonitor: KeyguardUpdateMonitor = kosmos.keyguardUpdateMonitor
+ val statusBarStateController: SysuiStatusBarStateController =
+ kosmos.fakeStatusBarStateController
val sensitiveNotificationProtectionController: SensitiveNotificationProtectionController =
- mock()
- val deviceEntryInteractor: DeviceEntryInteractor = mock()
- val sceneInteractor: SceneInteractor = mock()
+ kosmos.mockSensitiveNotificationProtectionController
+ val deviceEntryInteractor: DeviceEntryInteractor
+ get() = kosmos.deviceEntryInteractor
- val coordinator: SensitiveContentCoordinator =
- DaggerTestSensitiveContentCoordinatorComponent.factory()
- .create(
- dynamicPrivacyController,
- lockscreenUserManager,
- keyguardUpdateMonitor,
- statusBarStateController,
- keyguardStateController,
- mSelectedUserInteractor,
- sensitiveNotificationProtectionController,
- deviceEntryInteractor,
- sceneInteractor,
- kosmos.applicationCoroutineScope,
- )
- .coordinator
+ val faceAuthRepository = kosmos.fakeDeviceEntryFaceAuthRepository
+ val sceneInteractor: SceneInteractor
+ get() = kosmos.sceneInteractor
+
+ val coordinator: SensitiveContentCoordinator by lazy { kosmos.sensitiveContentCoordinator }
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
@Test
- fun onDynamicPrivacyChanged_invokeInvalidationListener() {
- coordinator.attach(pipeline)
- val invalidator =
- withArgCaptor<Invalidator> { verify(pipeline).addPreRenderInvalidator(capture()) }
- val dynamicPrivacyListener =
- withArgCaptor<DynamicPrivacyController.Listener> {
- verify(dynamicPrivacyController).addListener(capture())
- }
+ @EnableSceneContainer
+ fun onLockscreenDynamicallyUnlocked_invokeInvalidationListener() =
+ testScope.runTest {
+ // Setup
+ coordinator.attach(pipeline)
+ val invalidator =
+ withArgCaptor<Invalidator> { verify(pipeline).addPreRenderInvalidator(capture()) }
+ val invalidationListener = mock<Pluggable.PluggableListener<Invalidator>>()
+ invalidator.setInvalidationListener(invalidationListener)
- val invalidationListener = mock<Pluggable.PluggableListener<Invalidator>>()
- invalidator.setInvalidationListener(invalidationListener)
+ // Given the lockscreen is shown
+ setupLockScreen(canSwipeUp = false)
+ clearInvocations(invalidationListener)
- dynamicPrivacyListener.onDynamicPrivacyChanged()
+ // When the device gets unlocked
+ faceAuthRepository.isAuthenticated.value = true
+ runCurrent()
- verify(invalidationListener).onPluggableInvalidated(eq(invalidator), any())
- }
+ // Then the invalidationListener is called
+ verify(invalidationListener).onPluggableInvalidated(eq(invalidator), any())
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun onDynamicPrivacyChanged_invokeInvalidationListener() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val invalidator =
+ withArgCaptor<Invalidator> { verify(pipeline).addPreRenderInvalidator(capture()) }
+ val dynamicPrivacyListener =
+ withArgCaptor<DynamicPrivacyController.Listener> {
+ verify(dynamicPrivacyController).addListener(capture())
+ }
+
+ val invalidationListener = mock<Pluggable.PluggableListener<Invalidator>>()
+ invalidator.setInvalidationListener(invalidationListener)
+
+ dynamicPrivacyListener.onDynamicPrivacyChanged()
+
+ verify(invalidationListener).onPluggableInvalidated(eq(invalidator), any())
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onSensitiveStateChanged_invokeInvalidationListener() {
- coordinator.attach(pipeline)
- val invalidator =
- withArgCaptor<Invalidator> { verify(pipeline).addPreRenderInvalidator(capture()) }
- val onSensitiveStateChangedListener =
- withArgCaptor<Runnable> {
- verify(sensitiveNotificationProtectionController)
- .registerSensitiveStateListener(capture())
- }
+ fun onSensitiveStateChanged_invokeInvalidationListener() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val invalidator =
+ withArgCaptor<Invalidator> { verify(pipeline).addPreRenderInvalidator(capture()) }
+ val onSensitiveStateChangedListener =
+ withArgCaptor<Runnable> {
+ verify(sensitiveNotificationProtectionController)
+ .registerSensitiveStateListener(capture())
+ }
- val invalidationListener = mock<Pluggable.PluggableListener<Invalidator>>()
- invalidator.setInvalidationListener(invalidationListener)
+ val invalidationListener = mock<Pluggable.PluggableListener<Invalidator>>()
+ invalidator.setInvalidationListener(invalidationListener)
- onSensitiveStateChangedListener.run()
+ onSensitiveStateChangedListener.run()
- verify(invalidationListener).onPluggableInvalidated(eq(invalidator), any())
- }
+ verify(invalidationListener).onPluggableInvalidated(eq(invalidator), any())
+ }
@Test
@DisableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun screenshareSecretFilter_flagDisabled_filterNoAdded() {
- coordinator.attach(pipeline)
+ fun screenshareSecretFilter_flagDisabled_filterNoAdded() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
- verify(pipeline, never()).addFinalizeFilter(any(NotifFilter::class.java))
- }
+ verify(pipeline, never()).addFinalizeFilter(any())
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun screenshareSecretFilter_sensitiveInctive_noFiltersSecret() {
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(false)
+ fun screenshareSecretFilter_sensitiveInctive_noFiltersSecret() =
+ testScope.runTest {
+ whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
+ .thenReturn(false)
- coordinator.attach(pipeline)
- val filter = withArgCaptor<NotifFilter> { verify(pipeline).addFinalizeFilter(capture()) }
+ coordinator.attach(pipeline)
+ val filter =
+ withArgCaptor<NotifFilter> { verify(pipeline).addFinalizeFilter(capture()) }
- val defaultNotification = createNotificationEntry("test", false, false)
- val notificationWithSecretVisibility = createNotificationEntry("test", true, false)
- val notificationOnSecretChannel = createNotificationEntry("test", false, true)
+ val defaultNotification = createNotificationEntry("test", false, false)
+ val notificationWithSecretVisibility = createNotificationEntry("test", true, false)
+ val notificationOnSecretChannel = createNotificationEntry("test", false, true)
- assertFalse(filter.shouldFilterOut(defaultNotification, 0))
- assertFalse(filter.shouldFilterOut(notificationWithSecretVisibility, 0))
- assertFalse(filter.shouldFilterOut(notificationOnSecretChannel, 0))
- }
+ assertFalse(filter.shouldFilterOut(defaultNotification, 0))
+ assertFalse(filter.shouldFilterOut(notificationWithSecretVisibility, 0))
+ assertFalse(filter.shouldFilterOut(notificationOnSecretChannel, 0))
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun screenshareSecretFilter_sensitiveActive_filtersSecret() {
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
+ fun screenshareSecretFilter_sensitiveActive_filtersSecret() =
+ testScope.runTest {
+ whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
+ .thenReturn(true)
- coordinator.attach(pipeline)
- val filter = withArgCaptor<NotifFilter> { verify(pipeline).addFinalizeFilter(capture()) }
+ coordinator.attach(pipeline)
+ val filter =
+ withArgCaptor<NotifFilter> { verify(pipeline).addFinalizeFilter(capture()) }
- val defaultNotification = createNotificationEntry("test", false, false)
- val notificationWithSecretVisibility = createNotificationEntry("test", true, false)
- val notificationOnSecretChannel = createNotificationEntry("test", false, true)
+ val defaultNotification = createNotificationEntry("test", false, false)
+ val notificationWithSecretVisibility = createNotificationEntry("test", true, false)
+ val notificationOnSecretChannel = createNotificationEntry("test", false, true)
- assertFalse(filter.shouldFilterOut(defaultNotification, 0))
- assertTrue(filter.shouldFilterOut(notificationWithSecretVisibility, 0))
- assertTrue(filter.shouldFilterOut(notificationOnSecretChannel, 0))
- }
+ assertFalse(filter.shouldFilterOut(defaultNotification, 0))
+ assertTrue(filter.shouldFilterOut(notificationWithSecretVisibility, 0))
+ assertTrue(filter.shouldFilterOut(notificationOnSecretChannel, 0))
+ }
@Test
- fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, false)
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(true)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, false)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(false, false)
- }
+ verify(entry.representativeEntry!!).setSensitive(false, false)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction_sensitiveActive() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction_sensitiveActive() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, false)
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(true)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, false)
+ whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(false, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
- }
+ verify(entry.representativeEntry!!).setSensitive(false, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction_shouldProtectNotification() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction_shouldProtectNotification() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, false)
- whenever(
- sensitiveNotificationProtectionController.shouldProtectNotification(
- entry.getRepresentativeEntry()
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(true)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, false)
+ whenever(
+ sensitiveNotificationProtectionController.shouldProtectNotification(
+ entry.getRepresentativeEntry()
+ )
)
- )
- .thenReturn(true)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(true, false)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
- }
+ verify(entry.representativeEntry!!).setSensitive(true, false)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
+ }
@Test
- fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, true)
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(true)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(false, false)
- }
+ verify(entry.representativeEntry!!).setSensitive(false, false)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction_sensitiveActive() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction_sensitiveActive() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, true)
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(true)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, true)
+ whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(false, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
- }
+ verify(entry.representativeEntry!!).setSensitive(false, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction_shouldProtectNotification() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction_shouldProtectNotification() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, true)
- whenever(
- sensitiveNotificationProtectionController.shouldProtectNotification(
- entry.getRepresentativeEntry()
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(true)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, true)
+ whenever(
+ sensitiveNotificationProtectionController.shouldProtectNotification(
+ entry.getRepresentativeEntry()
+ )
)
- )
- .thenReturn(true)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(true, false)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
- }
+ verify(entry.representativeEntry!!).setSensitive(true, false)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
+ }
@Test
- fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, false)
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(true)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, false)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(false, false)
- }
+ verify(entry.representativeEntry!!).setSensitive(false, false)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs_sensitiveActive() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs_sensitiveActive() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, false)
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(true)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, false)
+ whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(false, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
- }
+ verify(entry.representativeEntry!!).setSensitive(false, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs_shouldProtectNotification() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs_shouldProtectNotification() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, false)
- whenever(
- sensitiveNotificationProtectionController.shouldProtectNotification(
- entry.getRepresentativeEntry()
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(true)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, false)
+ whenever(
+ sensitiveNotificationProtectionController.shouldProtectNotification(
+ entry.getRepresentativeEntry()
+ )
)
- )
- .thenReturn(true)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(true, false)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
- }
+ verify(entry.representativeEntry!!).setSensitive(true, false)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
+ }
@Test
- fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, false)
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, false)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(false, true)
- }
+ verify(entry.representativeEntry!!).setSensitive(false, true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
@Suppress("ktlint:standard:max-line-length")
- fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction_sensitiveActive() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction_sensitiveActive() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, false)
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, false)
+ whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(false, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
- }
+ verify(entry.representativeEntry!!).setSensitive(false, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
@Suppress("ktlint:standard:max-line-length")
- fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction_shouldProtectNotification() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction_shouldProtectNotification() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, false)
- whenever(
- sensitiveNotificationProtectionController.shouldProtectNotification(
- entry.getRepresentativeEntry()
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, false)
+ whenever(
+ sensitiveNotificationProtectionController.shouldProtectNotification(
+ entry.getRepresentativeEntry()
+ )
)
- )
- .thenReturn(true)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(true, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
- }
+ verify(entry.representativeEntry!!).setSensitive(true, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
+ }
@Test
- fun onBeforeRenderList_deviceLocked_notifNeedsRedaction() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceLocked_notifNeedsRedaction() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, true)
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(true, true)
- }
+ verify(entry.representativeEntry!!).setSensitive(true, true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onBeforeRenderList_deviceLocked_notifNeedsRedaction_sensitiveActive() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceLocked_notifNeedsRedaction_sensitiveActive() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, true)
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, true)
+ whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(true, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
- }
+ verify(entry.representativeEntry!!).setSensitive(true, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onBeforeRenderList_deviceLocked_notifNeedsRedaction_shouldProtectNotification() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceLocked_notifNeedsRedaction_shouldProtectNotification() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
- val entry = fakeNotification(1, true)
- whenever(
- sensitiveNotificationProtectionController.shouldProtectNotification(
- entry.getRepresentativeEntry()
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = false)
+ val entry = fakeNotification(1, true)
+ whenever(
+ sensitiveNotificationProtectionController.shouldProtectNotification(
+ entry.getRepresentativeEntry()
+ )
)
- )
- .thenReturn(true)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(true, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
- }
+ verify(entry.representativeEntry!!).setSensitive(true, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
+ }
@Test
- fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
- val entry = fakeNotification(1, true)
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = true)
+ val entry = fakeNotification(1, true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(false, true)
- }
+ verify(entry.representativeEntry!!).setSensitive(false, true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction_sensitiveActive() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction_sensitiveActive() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
- val entry = fakeNotification(1, true)
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = true)
+ val entry = fakeNotification(1, true)
+ whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(false, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
- }
+ verify(entry.representativeEntry!!).setSensitive(false, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
@Suppress("ktlint:standard:max-line-length")
- fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction_shouldProtectNotification() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction_shouldProtectNotification() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
- val entry = fakeNotification(1, true)
- whenever(
- sensitiveNotificationProtectionController.shouldProtectNotification(
- entry.getRepresentativeEntry()
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = true)
+ val entry = fakeNotification(1, true)
+ whenever(
+ sensitiveNotificationProtectionController.shouldProtectNotification(
+ entry.getRepresentativeEntry()
+ )
)
- )
- .thenReturn(true)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(true, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
- }
+ verify(entry.representativeEntry!!).setSensitive(true, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
+ }
@Test
- fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
- whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true)
- val entry = fakeNotification(2, true)
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = true)
+ whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true)
+ val entry = fakeNotification(2, true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(true, true)
- }
+ verify(entry.representativeEntry!!).setSensitive(true, true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
- fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge_sensitiveActive() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge_sensitiveActive() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
- whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true)
- val entry = fakeNotification(2, true)
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = true)
+ whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true)
+ val entry = fakeNotification(2, true)
+ whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(true, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
- }
+ verify(entry.representativeEntry!!).setSensitive(true, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
+ }
@Test
@EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
@Suppress("ktlint:standard:max-line-length")
- fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge_shouldProtectNotification() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge_shouldProtectNotification() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
- whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true)
- val entry = fakeNotification(2, true)
- whenever(
- sensitiveNotificationProtectionController.shouldProtectNotification(
- entry.getRepresentativeEntry()
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = true)
+ whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true)
+ val entry = fakeNotification(2, true)
+ whenever(
+ sensitiveNotificationProtectionController.shouldProtectNotification(
+ entry.getRepresentativeEntry()
+ )
)
- )
- .thenReturn(true)
+ .thenReturn(true)
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!).setSensitive(true, true)
- verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
- }
+ verify(entry.representativeEntry!!).setSensitive(true, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
+ }
@Test
- fun onBeforeRenderList_deviceDynamicallyUnlocked_deviceBiometricBypassingLockScreen() {
- coordinator.attach(pipeline)
- val onBeforeRenderListListener =
- withArgCaptor<OnBeforeRenderListListener> {
- verify(pipeline).addOnBeforeRenderListListener(capture())
- }
+ fun onBeforeRenderList_deviceDynamicallyUnlocked_deviceBiometricBypassingLockScreen() =
+ testScope.runTest {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener =
+ withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
- whenever(lockscreenUserManager.currentUserId).thenReturn(1)
- whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
- whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
- whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
- whenever(statusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD)
- whenever(keyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing(any()))
- .thenReturn(true)
- val entry = fakeNotification(2, true)
- whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
- whenever(sensitiveNotificationProtectionController.shouldProtectNotification(any()))
- .thenReturn(true)
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1))
+ .thenReturn(false)
+ setupLockScreen(canSwipeUp = true)
+ whenever(keyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing(any()))
+ .thenReturn(true)
+ val entry = fakeNotification(2, true)
+ whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
+ .thenReturn(true)
+ whenever(sensitiveNotificationProtectionController.shouldProtectNotification(any()))
+ .thenReturn(true)
+ statusBarStateController.state = StatusBarState.KEYGUARD
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
- verify(entry.representativeEntry!!, never()).setSensitive(any(), any())
- verify(entry.representativeEntry!!.row!!, never()).setPublicExpanderVisible(any())
+ verify(entry.representativeEntry!!, never()).setSensitive(any(), any())
+ verify(entry.representativeEntry!!.row!!, never()).setPublicExpanderVisible(any())
+ }
+
+ private fun TestScope.setupLockScreen(canSwipeUp: Boolean) {
+ if (SceneContainerFlag.isEnabled) {
+ val authMethod =
+ if (canSwipeUp) AuthenticationMethodModel.None
+ else AuthenticationMethodModel.Password
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
+ kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
+ switchToScene(Scenes.Lockscreen)
+ } else {
+ whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(canSwipeUp)
+ }
+ }
+
+ private fun TestScope.switchToScene(sceneKey: SceneKey) {
+ sceneInteractor.changeScene(sceneKey, "reason")
+ sceneInteractor.setTransitionState(flowOf(ObservableTransitionState.Idle(sceneKey)))
+ runCurrent()
}
private fun fakeNotification(notifUserId: Int, needsRedaction: Boolean): ListEntry {
@@ -733,26 +873,3 @@
return notificationEntry
}
}
-
-@CoordinatorScope
-@Component(modules = [SensitiveContentCoordinatorModule::class])
-interface TestSensitiveContentCoordinatorComponent {
- val coordinator: SensitiveContentCoordinator
-
- @Component.Factory
- interface Factory {
- fun create(
- @BindsInstance dynamicPrivacyController: DynamicPrivacyController,
- @BindsInstance lockscreenUserManager: NotificationLockscreenUserManager,
- @BindsInstance keyguardUpdateMonitor: KeyguardUpdateMonitor,
- @BindsInstance statusBarStateController: StatusBarStateController,
- @BindsInstance keyguardStateController: KeyguardStateController,
- @BindsInstance selectedUserInteractor: SelectedUserInteractor,
- @BindsInstance
- sensitiveNotificationProtectionController: SensitiveNotificationProtectionController,
- @BindsInstance deviceEntryInteractor: DeviceEntryInteractor,
- @BindsInstance sceneInteractor: SceneInteractor,
- @BindsInstance @Application scope: CoroutineScope,
- ): TestSensitiveContentCoordinatorComponent
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplOldTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplOldTest.kt
deleted file mode 100644
index d2a7c61..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplOldTest.kt
+++ /dev/null
@@ -1,705 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.statusbar.notification.headsup
-
-import android.app.Notification
-import android.app.PendingIntent
-import android.app.Person
-import android.os.Handler
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.FlagsParameterization
-import android.testing.TestableLooper.RunWithLooper
-import androidx.test.filters.SmallTest
-import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.kosmos.KosmosJavaAdapter
-import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.res.R
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
-import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManagerImpl.HeadsUpEntry
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
-import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.concurrency.mockExecutorHandler
-import com.android.systemui.util.kotlin.JavaAdapter
-import com.android.systemui.util.settings.FakeGlobalSettings
-import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.truth.Truth
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.MutableStateFlow
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.invocation.InvocationOnMock
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
-import org.mockito.kotlin.eq
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4
-import platform.test.runner.parameterized.Parameters
-
-@SmallTest
-@RunWithLooper
-@RunWith(ParameterizedAndroidJunit4::class)
-// TODO(b/378142453): Merge this with HeadsUpManagerImplTest.
-open class HeadsUpManagerImplOldTest(flags: FlagsParameterization?) : SysuiTestCase() {
- protected var mKosmos: KosmosJavaAdapter = KosmosJavaAdapter(this)
-
- @JvmField @Rule var rule: MockitoRule = MockitoJUnit.rule()
-
- private val mUiEventLoggerFake = UiEventLoggerFake()
-
- private val mLogger: HeadsUpManagerLogger = Mockito.spy(HeadsUpManagerLogger(logcatLogBuffer()))
-
- @Mock private val mBgHandler: Handler? = null
-
- @Mock private val dumpManager: DumpManager? = null
-
- @Mock private val mShadeInteractor: ShadeInteractor? = null
- private var mAvalancheController: AvalancheController? = null
-
- @Mock private val mAccessibilityMgr: AccessibilityManagerWrapper? = null
-
- protected val mGlobalSettings: FakeGlobalSettings = FakeGlobalSettings()
- protected val mSystemClock: FakeSystemClock = FakeSystemClock()
- protected val mExecutor: FakeExecutor = FakeExecutor(mSystemClock)
-
- @Mock protected var mRow: ExpandableNotificationRow? = null
-
- private fun createHeadsUpManager(): HeadsUpManagerImpl {
- return HeadsUpManagerImpl(
- mContext,
- mLogger,
- mKosmos.statusBarStateController,
- mKosmos.keyguardBypassController,
- GroupMembershipManagerImpl(),
- mKosmos.visualStabilityProvider,
- mKosmos.configurationController,
- mockExecutorHandler(mExecutor),
- mGlobalSettings,
- mSystemClock,
- mExecutor,
- mAccessibilityMgr,
- mUiEventLoggerFake,
- JavaAdapter(mKosmos.testScope),
- mShadeInteractor,
- mAvalancheController,
- )
- }
-
- private fun createStickyEntry(id: Int): NotificationEntry {
- val notif =
- Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setFullScreenIntent(
- Mockito.mock(PendingIntent::class.java), /* highPriority */
- true,
- )
- .build()
- return HeadsUpManagerTestUtil.createEntry(id, notif)
- }
-
- private fun createStickyForSomeTimeEntry(id: Int): NotificationEntry {
- val notif =
- Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setFlag(Notification.FLAG_FSI_REQUESTED_BUT_DENIED, true)
- .build()
- return HeadsUpManagerTestUtil.createEntry(id, notif)
- }
-
- private fun useAccessibilityTimeout(use: Boolean) {
- if (use) {
- Mockito.doReturn(TEST_A11Y_AUTO_DISMISS_TIME)
- .`when`(mAccessibilityMgr!!)
- .getRecommendedTimeoutMillis(ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt())
- } else {
- Mockito.`when`(
- mAccessibilityMgr!!.getRecommendedTimeoutMillis(
- ArgumentMatchers.anyInt(),
- ArgumentMatchers.anyInt(),
- )
- )
- .then { i: InvocationOnMock -> i.getArgument(0) }
- }
- }
-
- init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
- }
-
- @Throws(Exception::class)
- override fun SysuiSetup() {
- super.SysuiSetup()
- mContext.getOrCreateTestableResources().apply {
- this.addOverride(R.integer.ambient_notification_extension_time, TEST_EXTENSION_TIME)
- this.addOverride(R.integer.touch_acceptance_delay, TEST_TOUCH_ACCEPTANCE_TIME)
- this.addOverride(
- R.integer.heads_up_notification_minimum_time,
- TEST_MINIMUM_DISPLAY_TIME,
- )
- this.addOverride(
- R.integer.heads_up_notification_minimum_time_with_throttling,
- TEST_MINIMUM_DISPLAY_TIME,
- )
- this.addOverride(R.integer.heads_up_notification_decay, TEST_AUTO_DISMISS_TIME)
- this.addOverride(
- R.integer.sticky_heads_up_notification_time,
- TEST_STICKY_AUTO_DISMISS_TIME,
- )
- }
-
- mAvalancheController =
- AvalancheController(dumpManager!!, mUiEventLoggerFake, mLogger, mBgHandler!!)
- Mockito.`when`(mShadeInteractor!!.isAnyExpanded).thenReturn(MutableStateFlow(true))
- Mockito.`when`(mKosmos.keyguardBypassController.bypassEnabled).thenReturn(false)
- }
-
- @Test
- fun testHasNotifications_headsUpManagerMapNotEmpty_true() {
- val bhum = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- bhum.showNotification(entry)
-
- Truth.assertThat(bhum.mHeadsUpEntryMap).isNotEmpty()
- Truth.assertThat(bhum.hasNotifications()).isTrue()
- }
-
- @Test
- @EnableFlags(NotificationThrottleHun.FLAG_NAME)
- fun testHasNotifications_avalancheMapNotEmpty_true() {
- val bhum = createHeadsUpManager()
- val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- val headsUpEntry = bhum.createHeadsUpEntry(notifEntry)
- mAvalancheController!!.addToNext(headsUpEntry) {}
-
- Truth.assertThat(mAvalancheController!!.getWaitingEntryList()).isNotEmpty()
- Truth.assertThat(bhum.hasNotifications()).isTrue()
- }
-
- @Test
- @EnableFlags(NotificationThrottleHun.FLAG_NAME)
- fun testHasNotifications_false() {
- val bhum = createHeadsUpManager()
- Truth.assertThat(bhum.mHeadsUpEntryMap).isEmpty()
- Truth.assertThat(mAvalancheController!!.getWaitingEntryList()).isEmpty()
- Truth.assertThat(bhum.hasNotifications()).isFalse()
- }
-
- @Test
- @EnableFlags(NotificationThrottleHun.FLAG_NAME)
- fun testGetHeadsUpEntryList_includesAvalancheEntryList() {
- val bhum = createHeadsUpManager()
- val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- val headsUpEntry = bhum.createHeadsUpEntry(notifEntry)
- mAvalancheController!!.addToNext(headsUpEntry) {}
-
- Truth.assertThat(bhum.headsUpEntryList).contains(headsUpEntry)
- }
-
- @Test
- @EnableFlags(NotificationThrottleHun.FLAG_NAME)
- fun testGetHeadsUpEntry_returnsAvalancheEntry() {
- val bhum = createHeadsUpManager()
- val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- val headsUpEntry = bhum.createHeadsUpEntry(notifEntry)
- mAvalancheController!!.addToNext(headsUpEntry) {}
-
- Truth.assertThat(bhum.getHeadsUpEntry(notifEntry.key)).isEqualTo(headsUpEntry)
- }
-
- @Test
- fun testShowNotification_addsEntry() {
- val alm = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-
- alm.showNotification(entry)
-
- assertThat(alm.isHeadsUpEntry(entry.key)).isTrue()
- assertThat(alm.hasNotifications()).isTrue()
- assertThat(alm.getEntry(entry.key)).isEqualTo(entry)
- }
-
- @Test
- fun testShowNotification_autoDismisses() {
- val alm = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-
- alm.showNotification(entry)
- mSystemClock.advanceTime((TEST_AUTO_DISMISS_TIME * 3 / 2).toLong())
-
- assertThat(alm.isHeadsUpEntry(entry.key)).isFalse()
- }
-
- @Test
- fun testRemoveNotification_removeDeferred() {
- val alm = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-
- alm.showNotification(entry)
-
- val removedImmediately =
- alm.removeNotification(entry.key, /* releaseImmediately= */ false, "removeDeferred")
- assertThat(removedImmediately).isFalse()
- assertThat(alm.isHeadsUpEntry(entry.key)).isTrue()
- }
-
- @Test
- fun testRemoveNotification_forceRemove() {
- val alm = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-
- alm.showNotification(entry)
-
- val removedImmediately =
- alm.removeNotification(entry.key, /* releaseImmediately= */ true, "forceRemove")
- assertThat(removedImmediately).isTrue()
- assertThat(alm.isHeadsUpEntry(entry.key)).isFalse()
- }
-
- @Test
- fun testReleaseAllImmediately() {
- val alm = createHeadsUpManager()
- for (i in 0 until TEST_NUM_NOTIFICATIONS) {
- val entry = HeadsUpManagerTestUtil.createEntry(i, mContext)
- entry.row = mRow
- alm.showNotification(entry)
- }
-
- alm.releaseAllImmediately()
-
- assertThat(alm.allEntries.count()).isEqualTo(0)
- }
-
- @Test
- fun testCanRemoveImmediately_notShownLongEnough() {
- val alm = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-
- alm.showNotification(entry)
-
- // The entry has just been added so we should not remove immediately.
- assertThat(alm.canRemoveImmediately(entry.key)).isFalse()
- }
-
- @Test
- fun testHunRemovedLogging() {
- val hum = createHeadsUpManager()
- val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- val headsUpEntry = Mockito.mock(HeadsUpEntry::class.java)
- Mockito.`when`(headsUpEntry.pinnedStatus)
- .thenReturn(MutableStateFlow(PinnedStatus.NotPinned))
- headsUpEntry.mEntry = notifEntry
-
- hum.onEntryRemoved(headsUpEntry, "test")
-
- Mockito.verify(mLogger, Mockito.times(1)).logNotificationActuallyRemoved(eq(notifEntry))
- }
-
- @Test
- fun testShowNotification_autoDismissesIncludingTouchAcceptanceDelay() {
- val hum = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- useAccessibilityTimeout(false)
-
- hum.showNotification(entry)
- mSystemClock.advanceTime((TEST_TOUCH_ACCEPTANCE_TIME / 2 + TEST_AUTO_DISMISS_TIME).toLong())
-
- assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
- }
-
- @Test
- fun testShowNotification_autoDismissesWithDefaultTimeout() {
- val hum = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- useAccessibilityTimeout(false)
-
- hum.showNotification(entry)
- mSystemClock.advanceTime(
- (TEST_TOUCH_ACCEPTANCE_TIME +
- (TEST_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2)
- .toLong()
- )
-
- assertThat(hum.isHeadsUpEntry(entry.key)).isFalse()
- }
-
- @Test
- fun testShowNotification_stickyForSomeTime_autoDismissesWithStickyTimeout() {
- val hum = createHeadsUpManager()
- val entry = createStickyForSomeTimeEntry(/* id= */ 0)
- useAccessibilityTimeout(false)
-
- hum.showNotification(entry)
- mSystemClock.advanceTime(
- (TEST_TOUCH_ACCEPTANCE_TIME +
- (TEST_AUTO_DISMISS_TIME + TEST_STICKY_AUTO_DISMISS_TIME) / 2)
- .toLong()
- )
-
- assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
- }
-
- @Test
- fun testShowNotification_sticky_neverAutoDismisses() {
- val hum = createHeadsUpManager()
- val entry = createStickyEntry(/* id= */ 0)
- useAccessibilityTimeout(false)
-
- hum.showNotification(entry)
- mSystemClock.advanceTime(
- (TEST_TOUCH_ACCEPTANCE_TIME + 2 * TEST_A11Y_AUTO_DISMISS_TIME).toLong()
- )
-
- assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
- }
-
- @Test
- fun testShowNotification_autoDismissesWithAccessibilityTimeout() {
- val hum = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- useAccessibilityTimeout(true)
-
- hum.showNotification(entry)
- mSystemClock.advanceTime(
- (TEST_TOUCH_ACCEPTANCE_TIME +
- (TEST_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2)
- .toLong()
- )
-
- assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
- }
-
- @Test
- fun testShowNotification_stickyForSomeTime_autoDismissesWithAccessibilityTimeout() {
- val hum = createHeadsUpManager()
- val entry = createStickyForSomeTimeEntry(/* id= */ 0)
- useAccessibilityTimeout(true)
-
- hum.showNotification(entry)
- mSystemClock.advanceTime(
- (TEST_TOUCH_ACCEPTANCE_TIME +
- (TEST_STICKY_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2)
- .toLong()
- )
-
- assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
- }
-
- @Test
- fun testRemoveNotification_beforeMinimumDisplayTime() {
- val hum = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- useAccessibilityTimeout(false)
-
- hum.showNotification(entry)
-
- val removedImmediately =
- hum.removeNotification(
- entry.key,
- /* releaseImmediately = */ false,
- "beforeMinimumDisplayTime",
- )
- assertThat(removedImmediately).isFalse()
- assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
-
- mSystemClock.advanceTime(
- ((TEST_MINIMUM_DISPLAY_TIME + TEST_AUTO_DISMISS_TIME) / 2).toLong()
- )
-
- assertThat(hum.isHeadsUpEntry(entry.key)).isFalse()
- }
-
- @Test
- fun testRemoveNotification_afterMinimumDisplayTime() {
- val hum = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- useAccessibilityTimeout(false)
-
- hum.showNotification(entry)
- mSystemClock.advanceTime(
- ((TEST_MINIMUM_DISPLAY_TIME + TEST_AUTO_DISMISS_TIME) / 2).toLong()
- )
-
- assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
-
- val removedImmediately =
- hum.removeNotification(
- entry.key,
- /* releaseImmediately = */ false,
- "afterMinimumDisplayTime",
- )
- assertThat(removedImmediately).isTrue()
- assertThat(hum.isHeadsUpEntry(entry.key)).isFalse()
- }
-
- @Test
- fun testRemoveNotification_releaseImmediately() {
- val hum = createHeadsUpManager()
- val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-
- hum.showNotification(entry)
-
- val removedImmediately =
- hum.removeNotification(
- entry.key,
- /* releaseImmediately = */ true,
- "afterMinimumDisplayTime",
- )
- assertThat(removedImmediately).isTrue()
- assertThat(hum.isHeadsUpEntry(entry.key)).isFalse()
- }
-
- @Test
- fun testIsSticky_rowPinnedAndExpanded_true() {
- val hum = createHeadsUpManager()
- val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- Mockito.`when`(mRow!!.isPinned).thenReturn(true)
- notifEntry.row = mRow
-
- hum.showNotification(notifEntry)
-
- val headsUpEntry = hum.getHeadsUpEntry(notifEntry.key)
- headsUpEntry!!.setExpanded(true)
-
- assertThat(hum.isSticky(notifEntry.key)).isTrue()
- }
-
- @Test
- fun testIsSticky_remoteInputActive_true() {
- val hum = createHeadsUpManager()
- val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-
- hum.showNotification(notifEntry)
-
- val headsUpEntry = hum.getHeadsUpEntry(notifEntry.key)
- headsUpEntry!!.mRemoteInputActive = true
-
- assertThat(hum.isSticky(notifEntry.key)).isTrue()
- }
-
- @Test
- fun testIsSticky_hasFullScreenIntent_true() {
- val hum = createHeadsUpManager()
- val notifEntry = HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
-
- hum.showNotification(notifEntry)
-
- assertThat(hum.isSticky(notifEntry.key)).isTrue()
- }
-
- @Test
- fun testIsSticky_stickyForSomeTime_false() {
- val hum = createHeadsUpManager()
- val entry = createStickyForSomeTimeEntry(/* id= */ 0)
-
- hum.showNotification(entry)
-
- assertThat(hum.isSticky(entry.key)).isFalse()
- }
-
- @Test
- fun testIsSticky_false() {
- val hum = createHeadsUpManager()
- val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-
- hum.showNotification(notifEntry)
-
- val headsUpEntry = hum.getHeadsUpEntry(notifEntry.key)
- headsUpEntry!!.setExpanded(false)
- headsUpEntry.mRemoteInputActive = false
-
- assertThat(hum.isSticky(notifEntry.key)).isFalse()
- }
-
- @Test
- fun testCompareTo_withNullEntries() {
- val hum = createHeadsUpManager()
- val alertEntry = NotificationEntryBuilder().setTag("alert").build()
-
- hum.showNotification(alertEntry)
-
- assertThat(hum.compare(alertEntry, null)).isLessThan(0)
- assertThat(hum.compare(null, alertEntry)).isGreaterThan(0)
- assertThat(hum.compare(null, null)).isEqualTo(0)
- }
-
- @Test
- fun testCompareTo_withNonAlertEntries() {
- val hum = createHeadsUpManager()
-
- val nonAlertEntry1 = NotificationEntryBuilder().setTag("nae1").build()
- val nonAlertEntry2 = NotificationEntryBuilder().setTag("nae2").build()
- val alertEntry = NotificationEntryBuilder().setTag("alert").build()
- hum.showNotification(alertEntry)
-
- assertThat(hum.compare(alertEntry, nonAlertEntry1)).isLessThan(0)
- assertThat(hum.compare(nonAlertEntry1, alertEntry)).isGreaterThan(0)
- assertThat(hum.compare(nonAlertEntry1, nonAlertEntry2)).isEqualTo(0)
- }
-
- @Test
- fun testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() {
- val hum = createHeadsUpManager()
-
- val ongoingCall =
- hum.HeadsUpEntry(
- NotificationEntryBuilder()
- .setSbn(
- HeadsUpManagerTestUtil.createSbn(
- /* id = */ 0,
- Notification.Builder(mContext, "")
- .setCategory(Notification.CATEGORY_CALL)
- .setOngoing(true),
- )
- )
- .build()
- )
-
- val activeRemoteInput =
- hum.HeadsUpEntry(HeadsUpManagerTestUtil.createEntry(/* id= */ 1, mContext))
- activeRemoteInput.mRemoteInputActive = true
-
- assertThat(ongoingCall.compareTo(activeRemoteInput)).isLessThan(0)
- assertThat(activeRemoteInput.compareTo(ongoingCall)).isGreaterThan(0)
- }
-
- @Test
- fun testAlertEntryCompareTo_incomingCallLessThanActiveRemoteInput() {
- val hum = createHeadsUpManager()
-
- val person = Person.Builder().setName("person").build()
- val intent = Mockito.mock(PendingIntent::class.java)
- val incomingCall =
- hum.HeadsUpEntry(
- NotificationEntryBuilder()
- .setSbn(
- HeadsUpManagerTestUtil.createSbn(
- /* id = */ 0,
- Notification.Builder(mContext, "")
- .setStyle(
- Notification.CallStyle.forIncomingCall(person, intent, intent)
- ),
- )
- )
- .build()
- )
-
- val activeRemoteInput =
- hum.HeadsUpEntry(HeadsUpManagerTestUtil.createEntry(/* id= */ 1, mContext))
- activeRemoteInput.mRemoteInputActive = true
-
- assertThat(incomingCall.compareTo(activeRemoteInput)).isLessThan(0)
- assertThat(activeRemoteInput.compareTo(incomingCall)).isGreaterThan(0)
- }
-
- @Test
- @EnableFlags(NotificationThrottleHun.FLAG_NAME)
- fun testPinEntry_logsPeek_throttleEnabled() {
- val hum = createHeadsUpManager()
-
- // Needs full screen intent in order to be pinned
- val entryToPin =
- hum.HeadsUpEntry(
- HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
- )
-
- // Note: the standard way to show a notification would be calling showNotification rather
- // than onAlertEntryAdded. However, in practice showNotification in effect adds
- // the notification and then updates it; in order to not log twice, the entry needs
- // to have a functional ExpandableNotificationRow that can keep track of whether it's
- // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
- hum.onEntryAdded(entryToPin)
-
- assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(2)
- assertThat(
- AvalancheController.ThrottleEvent.AVALANCHE_THROTTLING_HUN_SHOWN.getId(),
- ).isEqualTo(mUiEventLoggerFake.eventId(0))
- assertThat(
- HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.id,
- ).isEqualTo(mUiEventLoggerFake.eventId(1))
- }
-
- @Test
- @DisableFlags(NotificationThrottleHun.FLAG_NAME)
- fun testPinEntry_logsPeek_throttleDisabled() {
- val hum = createHeadsUpManager()
-
- // Needs full screen intent in order to be pinned
- val entryToPin =
- hum.HeadsUpEntry(
- HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
- )
-
- // Note: the standard way to show a notification would be calling showNotification rather
- // than onAlertEntryAdded. However, in practice showNotification in effect adds
- // the notification and then updates it; in order to not log twice, the entry needs
- // to have a functional ExpandableNotificationRow that can keep track of whether it's
- // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
- hum.onEntryAdded(entryToPin)
-
- assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(1)
- assertThat(
- HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.id,
- ).isEqualTo(mUiEventLoggerFake.eventId(0))
- }
-
- @Test
- fun testSetUserActionMayIndirectlyRemove() {
- val hum = createHeadsUpManager()
- val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
-
- hum.showNotification(notifEntry)
-
- assertThat(hum.canRemoveImmediately(notifEntry.key)).isFalse()
-
- hum.setUserActionMayIndirectlyRemove(notifEntry)
-
- assertThat(hum.canRemoveImmediately(notifEntry.key)).isTrue()
- }
-
- companion object {
- const val TEST_TOUCH_ACCEPTANCE_TIME: Int = 200
- const val TEST_A11Y_AUTO_DISMISS_TIME: Int = 1000
- const val TEST_EXTENSION_TIME = 500
-
- const val TEST_MINIMUM_DISPLAY_TIME: Int = 400
- const val TEST_AUTO_DISMISS_TIME: Int = 600
- const val TEST_STICKY_AUTO_DISMISS_TIME: Int = 800
-
- // Number of notifications to use in tests requiring multiple notifications
- private const val TEST_NUM_NOTIFICATIONS = 4
-
- init {
- Truth.assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME)
- Truth.assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME)
- Truth.assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_A11Y_AUTO_DISMISS_TIME)
- }
-
- @get:Parameters(name = "{0}")
- @JvmStatic
- val flags: List<FlagsParameterization>
- get() = FlagsParameterization.allCombinationsOf(NotificationThrottleHun.FLAG_NAME)
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt
index 96f0201..8420c49 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt
@@ -15,25 +15,38 @@
*/
package com.android.systemui.statusbar.notification.headsup
+import android.app.Notification
+import android.app.PendingIntent
+import android.app.Person
import android.os.Handler
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
+import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
+import android.view.accessibility.accessibilityManager
import android.view.accessibility.accessibilityManagerWrapper
import androidx.test.filters.SmallTest
import com.android.internal.logging.uiEventLoggerFake
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.dump.dumpManager
+import com.android.systemui.flags.BrokenWithSceneContainer
import com.android.systemui.flags.andSceneContainer
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
-import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.provider.visualStabilityProvider
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManagerImpl.HeadsUpEntry
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
import com.android.systemui.statusbar.phone.keyguardBypassController
import com.android.systemui.statusbar.policy.configurationController
@@ -41,14 +54,19 @@
import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.mockExecutorHandler
import com.android.systemui.util.kotlin.JavaAdapter
+import com.android.systemui.util.settings.fakeGlobalSettings
+import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
-import junit.framework.Assert
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
@@ -56,20 +74,27 @@
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
@RunWithLooper
-class HeadsUpManagerImplTest(flags: FlagsParameterization) : HeadsUpManagerImplOldTest(flags) {
-
- private val mHeadsUpManagerLogger = HeadsUpManagerLogger(logcatLogBuffer())
+class HeadsUpManagerImplTest(flags: FlagsParameterization) : SysuiTestCase() {
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val testScope = kosmos.testScope
- private val mGroupManager = mock<GroupMembershipManager>()
- private val mBgHandler = mock<Handler>()
+ private val groupManager = mock<GroupMembershipManager>()
+ private val bgHandler = mock<Handler>()
+ private val headsUpManagerLogger = mock<HeadsUpManagerLogger>()
val statusBarStateController = kosmos.sysuiStatusBarStateController
- private val mJavaAdapter: JavaAdapter = JavaAdapter(testScope.backgroundScope)
+ private val globalSettings = kosmos.fakeGlobalSettings
+ private val systemClock = kosmos.fakeSystemClock
+ private val executor = kosmos.fakeExecutor
+ private val uiEventLoggerFake = kosmos.uiEventLoggerFake
+ private val javaAdapter: JavaAdapter = JavaAdapter(testScope.backgroundScope)
- private lateinit var mAvalancheController: AvalancheController
+ private lateinit var testHelper: NotificationTestHelper
+ private lateinit var avalancheController: AvalancheController
private lateinit var underTest: HeadsUpManagerImpl
@Before
@@ -92,42 +117,259 @@
)
}
+ allowTestableLooperAsMainThread()
+ testHelper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
+
whenever(kosmos.keyguardBypassController.bypassEnabled).thenReturn(false)
kosmos.visualStabilityProvider.isReorderingAllowed = true
- mAvalancheController =
+ avalancheController =
AvalancheController(
kosmos.dumpManager,
- kosmos.uiEventLoggerFake,
- mHeadsUpManagerLogger,
- mBgHandler,
+ uiEventLoggerFake,
+ headsUpManagerLogger,
+ bgHandler,
)
underTest =
HeadsUpManagerImpl(
mContext,
- mHeadsUpManagerLogger,
+ headsUpManagerLogger,
statusBarStateController,
kosmos.keyguardBypassController,
- mGroupManager,
+ groupManager,
kosmos.visualStabilityProvider,
kosmos.configurationController,
- mockExecutorHandler(mExecutor),
- mGlobalSettings,
- mSystemClock,
- mExecutor,
+ mockExecutorHandler(executor),
+ globalSettings,
+ systemClock,
+ executor,
kosmos.accessibilityManagerWrapper,
- kosmos.uiEventLoggerFake,
- mJavaAdapter,
+ uiEventLoggerFake,
+ javaAdapter,
kosmos.shadeInteractor,
- mAvalancheController,
+ avalancheController,
)
}
@Test
+ fun testHasNotifications_headsUpManagerMapNotEmpty_true() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ underTest.showNotification(entry)
+
+ assertThat(underTest.mHeadsUpEntryMap).isNotEmpty()
+ assertThat(underTest.hasNotifications()).isTrue()
+ }
+
+ @Test
+ @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+ fun testHasNotifications_avalancheMapNotEmpty_true() {
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ val headsUpEntry = underTest.createHeadsUpEntry(notifEntry)
+ avalancheController.addToNext(headsUpEntry) {}
+
+ assertThat(avalancheController.getWaitingEntryList()).isNotEmpty()
+ assertThat(underTest.hasNotifications()).isTrue()
+ }
+
+ @Test
+ @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+ fun testHasNotifications_false() {
+ assertThat(underTest.mHeadsUpEntryMap).isEmpty()
+ assertThat(avalancheController.getWaitingEntryList()).isEmpty()
+ assertThat(underTest.hasNotifications()).isFalse()
+ }
+
+ @Test
+ @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+ fun testGetHeadsUpEntryList_includesAvalancheEntryList() {
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ val headsUpEntry = underTest.createHeadsUpEntry(notifEntry)
+ avalancheController.addToNext(headsUpEntry) {}
+
+ assertThat(underTest.headsUpEntryList).contains(headsUpEntry)
+ }
+
+ @Test
+ @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+ fun testGetHeadsUpEntry_returnsAvalancheEntry() {
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ val headsUpEntry = underTest.createHeadsUpEntry(notifEntry)
+ avalancheController.addToNext(headsUpEntry) {}
+
+ assertThat(underTest.getHeadsUpEntry(notifEntry.key)).isEqualTo(headsUpEntry)
+ }
+
+ @Test
+ fun testShowNotification_addsEntry() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(entry)
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+ assertThat(underTest.hasNotifications()).isTrue()
+ assertThat(underTest.getEntry(entry.key)).isEqualTo(entry)
+ }
+
+ @Test
+ fun testShowNotification_autoDismisses() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(entry)
+ systemClock.advanceTime((TEST_AUTO_DISMISS_TIME * 3 / 2).toLong())
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testRemoveNotification_removeDeferred() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(entry)
+
+ val removedImmediately =
+ underTest.removeNotification(
+ entry.key,
+ /* releaseImmediately= */ false,
+ "removeDeferred",
+ )
+ assertThat(removedImmediately).isFalse()
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+ }
+
+ @Test
+ fun testRemoveNotification_forceRemove() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(entry)
+
+ val removedImmediately =
+ underTest.removeNotification(entry.key, /* releaseImmediately= */ true, "forceRemove")
+ assertThat(removedImmediately).isTrue()
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testReleaseAllImmediately() {
+ for (i in 0 until 4) {
+ val entry = HeadsUpManagerTestUtil.createEntry(i, mContext)
+ entry.row = mock<ExpandableNotificationRow>()
+ underTest.showNotification(entry)
+ }
+
+ underTest.releaseAllImmediately()
+
+ assertThat(underTest.allEntries.count()).isEqualTo(0)
+ }
+
+ @Test
+ fun testCanRemoveImmediately_notShownLongEnough() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(entry)
+
+ // The entry has just been added so we should not remove immediately.
+ assertThat(underTest.canRemoveImmediately(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testHunRemovedLogging() {
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ val headsUpEntry = underTest.HeadsUpEntry(notifEntry)
+ headsUpEntry.setRowPinnedStatus(PinnedStatus.NotPinned)
+
+ underTest.onEntryRemoved(headsUpEntry, "test")
+
+ verify(headsUpManagerLogger, times(1)).logNotificationActuallyRemoved(eq(notifEntry))
+ }
+
+ @Test
+ fun testShowNotification_autoDismissesIncludingTouchAcceptanceDelay() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ useAccessibilityTimeout(false)
+
+ underTest.showNotification(entry)
+ systemClock.advanceTime((TEST_TOUCH_ACCEPTANCE_TIME / 2 + TEST_AUTO_DISMISS_TIME).toLong())
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+ }
+
+ @Test
+ fun testShowNotification_autoDismissesWithDefaultTimeout() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ useAccessibilityTimeout(false)
+
+ underTest.showNotification(entry)
+ systemClock.advanceTime(
+ (TEST_TOUCH_ACCEPTANCE_TIME +
+ (TEST_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2)
+ .toLong()
+ )
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testRemoveNotification_beforeMinimumDisplayTime() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ useAccessibilityTimeout(false)
+
+ underTest.showNotification(entry)
+
+ val removedImmediately =
+ underTest.removeNotification(
+ entry.key,
+ /* releaseImmediately = */ false,
+ "beforeMinimumDisplayTime",
+ )
+ assertThat(removedImmediately).isFalse()
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+
+ systemClock.advanceTime(((TEST_MINIMUM_DISPLAY_TIME + TEST_AUTO_DISMISS_TIME) / 2).toLong())
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testRemoveNotification_afterMinimumDisplayTime() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ useAccessibilityTimeout(false)
+
+ underTest.showNotification(entry)
+ systemClock.advanceTime(((TEST_MINIMUM_DISPLAY_TIME + TEST_AUTO_DISMISS_TIME) / 2).toLong())
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+
+ val removedImmediately =
+ underTest.removeNotification(
+ entry.key,
+ /* releaseImmediately = */ false,
+ "afterMinimumDisplayTime",
+ )
+ assertThat(removedImmediately).isTrue()
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testRemoveNotification_releaseImmediately() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(entry)
+
+ val removedImmediately =
+ underTest.removeNotification(
+ entry.key,
+ /* releaseImmediately = */ true,
+ "afterMinimumDisplayTime",
+ )
+ assertThat(removedImmediately).isTrue()
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
+ }
+
+ @Test
fun testSnooze() {
val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
underTest.showNotification(entry)
underTest.snooze()
- Assert.assertTrue(underTest.isSnoozed(entry.sbn.packageName))
+ assertThat(underTest.isSnoozed(entry.sbn.packageName)).isTrue()
}
@Test
@@ -143,8 +385,8 @@
/* releaseImmediately= */ false,
/* reason= */ "swipe out",
)
- Assert.assertTrue(removedImmediately)
- Assert.assertFalse(underTest.isHeadsUpEntry(entry.key))
+ assertThat(removedImmediately).isTrue()
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isFalse()
}
@Test
@@ -154,7 +396,7 @@
underTest.addSwipedOutNotification(entry.key)
// Notification is swiped so it can be immediately removed.
- Assert.assertTrue(underTest.canRemoveImmediately(entry.key))
+ assertThat(underTest.canRemoveImmediately(entry.key)).isTrue()
}
@Ignore("b/141538055")
@@ -162,12 +404,12 @@
fun testCanRemoveImmediately_notTopEntry() {
val earlierEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
val laterEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 1, mContext)
- laterEntry.row = mRow
+ laterEntry.row = mock<ExpandableNotificationRow>()
underTest.showNotification(earlierEntry)
underTest.showNotification(laterEntry)
// Notification is "behind" a higher priority notification so we can remove it immediately.
- Assert.assertTrue(underTest.canRemoveImmediately(earlierEntry.key))
+ assertThat(underTest.canRemoveImmediately(earlierEntry.key)).isTrue()
}
@Test
@@ -175,8 +417,8 @@
val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
underTest.showNotification(entry)
underTest.extendHeadsUp()
- mSystemClock.advanceTime(((TEST_AUTO_DISMISS_TIME + TEST_EXTENSION_TIME) / 2).toLong())
- Assert.assertTrue(underTest.isHeadsUpEntry(entry.key))
+ systemClock.advanceTime(((TEST_AUTO_DISMISS_TIME + TEST_EXTENSION_TIME) / 2).toLong())
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
}
@Test
@@ -228,13 +470,129 @@
}
@Test
+ fun testShowNotification_sticky_neverAutoDismisses() {
+ val entry = createStickyEntry(id = 0)
+ useAccessibilityTimeout(false)
+
+ underTest.showNotification(entry)
+ systemClock.advanceTime(
+ (TEST_TOUCH_ACCEPTANCE_TIME + 2 * TEST_A11Y_AUTO_DISMISS_TIME).toLong()
+ )
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+ }
+
+ @Test
+ fun testShowNotification_autoDismissesWithAccessibilityTimeout() {
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ useAccessibilityTimeout(true)
+
+ underTest.showNotification(entry)
+ systemClock.advanceTime(
+ (TEST_TOUCH_ACCEPTANCE_TIME +
+ (TEST_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2)
+ .toLong()
+ )
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+ }
+
+ @Test
+ fun testShowNotification_stickyForSomeTime_autoDismissesWithStickyTimeout() {
+ val entry = createStickyForSomeTimeEntry(id = 0)
+ useAccessibilityTimeout(false)
+
+ underTest.showNotification(entry)
+ systemClock.advanceTime(
+ (TEST_TOUCH_ACCEPTANCE_TIME +
+ (TEST_AUTO_DISMISS_TIME + TEST_STICKY_AUTO_DISMISS_TIME) / 2)
+ .toLong()
+ )
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+ }
+
+ @Test
+ fun testShowNotification_stickyForSomeTime_autoDismissesWithAccessibilityTimeout() {
+ val entry = createStickyForSomeTimeEntry(id = 0)
+ useAccessibilityTimeout(true)
+
+ underTest.showNotification(entry)
+ systemClock.advanceTime(
+ (TEST_TOUCH_ACCEPTANCE_TIME +
+ (TEST_STICKY_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2)
+ .toLong()
+ )
+
+ assertThat(underTest.isHeadsUpEntry(entry.key)).isTrue()
+ }
+
+ @Test
+ fun testIsSticky_rowPinnedAndExpanded_true() {
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ val row = testHelper.createRow()
+ row.setPinnedStatus(PinnedStatus.PinnedBySystem)
+ notifEntry.row = row
+
+ underTest.showNotification(notifEntry)
+
+ val headsUpEntry = underTest.getHeadsUpEntry(notifEntry.key)
+ headsUpEntry!!.setExpanded(true)
+
+ assertThat(underTest.isSticky(notifEntry.key)).isTrue()
+ }
+
+ @Test
+ fun testIsSticky_remoteInputActive_true() {
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(notifEntry)
+
+ val headsUpEntry = underTest.getHeadsUpEntry(notifEntry.key)
+ headsUpEntry!!.mRemoteInputActive = true
+
+ assertThat(underTest.isSticky(notifEntry.key)).isTrue()
+ }
+
+ @Test
+ fun testIsSticky_hasFullScreenIntent_true() {
+ val notifEntry = HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(notifEntry)
+
+ assertThat(underTest.isSticky(notifEntry.key)).isTrue()
+ }
+
+ @Test
+ fun testIsSticky_stickyForSomeTime_false() {
+ val entry = createStickyForSomeTimeEntry(id = 0)
+
+ underTest.showNotification(entry)
+
+ assertThat(underTest.isSticky(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testIsSticky_false() {
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(notifEntry)
+
+ val headsUpEntry = underTest.getHeadsUpEntry(notifEntry.key)
+ headsUpEntry!!.setExpanded(false)
+ headsUpEntry.mRemoteInputActive = false
+
+ assertThat(underTest.isSticky(notifEntry.key)).isFalse()
+ }
+
+ @Test
fun testShouldHeadsUpBecomePinned_noFSI_false() =
kosmos.runTest {
statusBarStateController.setState(StatusBarState.KEYGUARD)
val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
- Assert.assertFalse(underTest.shouldHeadsUpBecomePinned(entry))
+ assertThat(underTest.shouldHeadsUpBecomePinned(entry)).isFalse()
}
@Test
@@ -251,7 +609,7 @@
val headsUpEntry = underTest.getHeadsUpEntry(notifEntry.key)
headsUpEntry!!.mWasUnpinned = false
- Assert.assertTrue(underTest.shouldHeadsUpBecomePinned(notifEntry))
+ assertThat(underTest.shouldHeadsUpBecomePinned(notifEntry)).isTrue()
}
@Test
@@ -268,22 +626,24 @@
val headsUpEntry = underTest.getHeadsUpEntry(notifEntry.key)
headsUpEntry!!.mWasUnpinned = true
- Assert.assertFalse(underTest.shouldHeadsUpBecomePinned(notifEntry))
+ assertThat(underTest.shouldHeadsUpBecomePinned(notifEntry)).isFalse()
}
@Test
+ @BrokenWithSceneContainer(381869885) // because `ShadeTestUtil.setShadeExpansion(0f)`
+ // still causes `ShadeInteractor.isAnyExpanded` to emit `true`, when it should emit `false`.
fun shouldHeadsUpBecomePinned_shadeNotExpanded_true() =
kosmos.runTest {
// GIVEN
- shadeTestUtil.setShadeExpansion(0f)
- // TODO(b/381869885): Determine why we need both of these ShadeTestUtil calls.
+ // TODO(b/381869885): We should be able to use `ShadeTestUtil.setShadeExpansion(0f)`
+ // instead.
shadeTestUtil.setLegacyExpandedOrAwaitingInputTransfer(false)
val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
statusBarStateController.setState(StatusBarState.SHADE)
// THEN
- Assert.assertTrue(underTest.shouldHeadsUpBecomePinned(entry))
+ assertThat(underTest.shouldHeadsUpBecomePinned(entry)).isTrue()
}
@Test
@@ -294,7 +654,7 @@
statusBarStateController.setState(StatusBarState.SHADE_LOCKED)
// THEN
- Assert.assertFalse(underTest.shouldHeadsUpBecomePinned(entry))
+ assertThat(underTest.shouldHeadsUpBecomePinned(entry)).isFalse()
}
@Test
@@ -305,7 +665,7 @@
statusBarStateController.setState(1207)
// THEN
- Assert.assertFalse(underTest.shouldHeadsUpBecomePinned(entry))
+ assertThat(underTest.shouldHeadsUpBecomePinned(entry)).isFalse()
}
@Test
@@ -318,7 +678,7 @@
statusBarStateController.setState(StatusBarState.KEYGUARD)
// THEN
- Assert.assertTrue(underTest.shouldHeadsUpBecomePinned(entry))
+ assertThat(underTest.shouldHeadsUpBecomePinned(entry)).isTrue()
}
@Test
@@ -331,7 +691,7 @@
statusBarStateController.setState(StatusBarState.KEYGUARD)
// THEN
- Assert.assertFalse(underTest.shouldHeadsUpBecomePinned(entry))
+ assertThat(underTest.shouldHeadsUpBecomePinned(entry)).isFalse()
}
@Test
@@ -346,11 +706,186 @@
statusBarStateController.setState(StatusBarState.SHADE)
// THEN
- Assert.assertFalse(underTest.shouldHeadsUpBecomePinned(entry))
+ assertThat(underTest.shouldHeadsUpBecomePinned(entry)).isFalse()
}
+ @Test
+ fun testCompareTo_withNullEntries() {
+ val alertEntry = NotificationEntryBuilder().setTag("alert").build()
+
+ underTest.showNotification(alertEntry)
+
+ assertThat(underTest.compare(alertEntry, null)).isLessThan(0)
+ assertThat(underTest.compare(null, alertEntry)).isGreaterThan(0)
+ assertThat(underTest.compare(null, null)).isEqualTo(0)
+ }
+
+ @Test
+ fun testCompareTo_withNonAlertEntries() {
+ val nonAlertEntry1 = NotificationEntryBuilder().setTag("nae1").build()
+ val nonAlertEntry2 = NotificationEntryBuilder().setTag("nae2").build()
+ val alertEntry = NotificationEntryBuilder().setTag("alert").build()
+ underTest.showNotification(alertEntry)
+
+ assertThat(underTest.compare(alertEntry, nonAlertEntry1)).isLessThan(0)
+ assertThat(underTest.compare(nonAlertEntry1, alertEntry)).isGreaterThan(0)
+ assertThat(underTest.compare(nonAlertEntry1, nonAlertEntry2)).isEqualTo(0)
+ }
+
+ @Test
+ fun testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() {
+ val ongoingCall =
+ underTest.HeadsUpEntry(
+ NotificationEntryBuilder()
+ .setSbn(
+ HeadsUpManagerTestUtil.createSbn(
+ /* id = */ 0,
+ Notification.Builder(mContext, "")
+ .setCategory(Notification.CATEGORY_CALL)
+ .setOngoing(true),
+ )
+ )
+ .build()
+ )
+
+ val activeRemoteInput =
+ underTest.HeadsUpEntry(HeadsUpManagerTestUtil.createEntry(/* id= */ 1, mContext))
+ activeRemoteInput.mRemoteInputActive = true
+
+ assertThat(ongoingCall.compareTo(activeRemoteInput)).isLessThan(0)
+ assertThat(activeRemoteInput.compareTo(ongoingCall)).isGreaterThan(0)
+ }
+
+ @Test
+ fun testAlertEntryCompareTo_incomingCallLessThanActiveRemoteInput() {
+ val person = Person.Builder().setName("person").build()
+ val intent = mock<PendingIntent>()
+ val incomingCall =
+ underTest.HeadsUpEntry(
+ NotificationEntryBuilder()
+ .setSbn(
+ HeadsUpManagerTestUtil.createSbn(
+ /* id = */ 0,
+ Notification.Builder(mContext, "")
+ .setStyle(
+ Notification.CallStyle.forIncomingCall(person, intent, intent)
+ ),
+ )
+ )
+ .build()
+ )
+
+ val activeRemoteInput =
+ underTest.HeadsUpEntry(HeadsUpManagerTestUtil.createEntry(/* id= */ 1, mContext))
+ activeRemoteInput.mRemoteInputActive = true
+
+ assertThat(incomingCall.compareTo(activeRemoteInput)).isLessThan(0)
+ assertThat(activeRemoteInput.compareTo(incomingCall)).isGreaterThan(0)
+ }
+
+ @Test
+ @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+ fun testPinEntry_logsPeek_throttleEnabled() {
+ // Needs full screen intent in order to be pinned
+ val entryToPin =
+ underTest.HeadsUpEntry(
+ HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
+ )
+
+ // Note: the standard way to show a notification would be calling showNotification rather
+ // than onAlertEntryAdded. However, in practice showNotification in effect adds
+ // the notification and then updates it; in order to not log twice, the entry needs
+ // to have a functional ExpandableNotificationRow that can keep track of whether it's
+ // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
+ underTest.onEntryAdded(entryToPin)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(2)
+ assertThat(AvalancheController.ThrottleEvent.AVALANCHE_THROTTLING_HUN_SHOWN.getId())
+ .isEqualTo(uiEventLoggerFake.eventId(0))
+ assertThat(HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.id)
+ .isEqualTo(uiEventLoggerFake.eventId(1))
+ }
+
+ @Test
+ @DisableFlags(NotificationThrottleHun.FLAG_NAME)
+ fun testPinEntry_logsPeek_throttleDisabled() {
+ // Needs full screen intent in order to be pinned
+ val entryToPin =
+ underTest.HeadsUpEntry(
+ HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
+ )
+
+ // Note: the standard way to show a notification would be calling showNotification rather
+ // than onAlertEntryAdded. However, in practice showNotification in effect adds
+ // the notification and then updates it; in order to not log twice, the entry needs
+ // to have a functional ExpandableNotificationRow that can keep track of whether it's
+ // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
+ underTest.onEntryAdded(entryToPin)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.id)
+ .isEqualTo(uiEventLoggerFake.eventId(0))
+ }
+
+ @Test
+ fun testSetUserActionMayIndirectlyRemove() {
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ underTest.showNotification(notifEntry)
+
+ assertThat(underTest.canRemoveImmediately(notifEntry.key)).isFalse()
+
+ underTest.setUserActionMayIndirectlyRemove(notifEntry)
+
+ assertThat(underTest.canRemoveImmediately(notifEntry.key)).isTrue()
+ }
+
+ private fun createStickyEntry(id: Int): NotificationEntry {
+ val notif =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setFullScreenIntent(mock<PendingIntent>(), /* highPriority= */ true)
+ .build()
+ return HeadsUpManagerTestUtil.createEntry(id, notif)
+ }
+
+ private fun createStickyForSomeTimeEntry(id: Int): NotificationEntry {
+ val notif =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setFlag(Notification.FLAG_FSI_REQUESTED_BUT_DENIED, true)
+ .build()
+ return HeadsUpManagerTestUtil.createEntry(id, notif)
+ }
+
+ private fun useAccessibilityTimeout(use: Boolean) {
+ if (use) {
+ whenever(kosmos.accessibilityManager.getRecommendedTimeoutMillis(any(), any()))
+ .thenReturn(TEST_A11Y_AUTO_DISMISS_TIME)
+ } else {
+ doAnswer { it.getArgument(0) as Int }
+ .whenever(kosmos.accessibilityManager)
+ .getRecommendedTimeoutMillis(any(), any())
+ }
+ }
+
companion object {
+ const val TEST_TOUCH_ACCEPTANCE_TIME = 200
+ const val TEST_A11Y_AUTO_DISMISS_TIME = 1000
+ const val TEST_EXTENSION_TIME = 500
+
+ const val TEST_MINIMUM_DISPLAY_TIME = 400
+ const val TEST_AUTO_DISMISS_TIME = 600
+ const val TEST_STICKY_AUTO_DISMISS_TIME = 800
+
+ init {
+ assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME)
+ assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME)
+ assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_A11Y_AUTO_DISMISS_TIME)
+ }
+
@get:Parameters(name = "{0}")
+ @JvmStatic
val flags: List<FlagsParameterization>
get() = buildList {
addAll(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index ca0f9ef..8bca17f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -28,7 +28,7 @@
import com.android.internal.logging.UiEventLogger
import com.android.internal.statusbar.IStatusBarService
import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.PluginManager
@@ -57,9 +57,7 @@
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.time.SystemClock
-import com.android.systemui.wmshell.BubblesManager
import com.google.android.msdl.domain.MSDLPlayer
-import java.util.Optional
import junit.framework.Assert
import org.junit.After
import org.junit.Before
@@ -103,9 +101,8 @@
private val gutsManager: NotificationGutsManager = mock()
private val onUserInteractionCallback: OnUserInteractionCallback = mock()
private val falsingManager: FalsingManager = mock()
- private val featureFlags: FeatureFlags = mock()
+ private val featureFlags: FeatureFlagsClassic = mock()
private val peopleNotificationIdentifier: PeopleNotificationIdentifier = mock()
- private val bubblesManager: BubblesManager = mock()
private val settingsController: NotificationSettingsController = mock()
private val dragController: ExpandableNotificationRowDragController = mock()
private val dismissibilityProvider: NotificationDismissibilityProvider = mock()
@@ -147,7 +144,6 @@
falsingManager,
featureFlags,
peopleNotificationIdentifier,
- Optional.of(bubblesManager),
settingsController,
dragController,
dismissibilityProvider,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 080ac3f..b323ef8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -25,7 +25,6 @@
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
import static com.android.systemui.util.Assert.runWithCurrentThreadAsMainThread;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -59,8 +58,8 @@
import com.android.keyguard.TestScopeProvider;
import com.android.systemui.TestableDependency;
import com.android.systemui.classifier.FalsingManagerFake;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FakeFeatureFlagsClassic;
+import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.media.controls.util.MediaFeatureFlag;
import com.android.systemui.media.dialog.MediaOutputDialogManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -71,7 +70,6 @@
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
-import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
@@ -79,6 +77,7 @@
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.icon.IconBuilder;
import com.android.systemui.statusbar.notification.icon.IconManager;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
@@ -89,7 +88,6 @@
import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
import com.android.systemui.statusbar.policy.SmartReplyConstants;
@@ -99,7 +97,6 @@
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.time.SystemClock;
import com.android.systemui.util.time.SystemClockImpl;
-import com.android.systemui.wmshell.BubblesManager;
import com.android.systemui.wmshell.BubblesTestActivity;
import kotlin.coroutines.CoroutineContext;
@@ -109,7 +106,6 @@
import org.mockito.ArgumentCaptor;
import java.util.Objects;
-import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -149,7 +145,7 @@
private final NotificationDismissibilityProvider mDismissibilityProvider;
public final Runnable mFutureDismissalRunnable;
private @InflationFlag int mDefaultInflationFlags;
- private final FakeFeatureFlags mFeatureFlags;
+ private final FakeFeatureFlagsClassic mFeatureFlags;
private final SystemClock mSystemClock;
private final RowInflaterTaskLogger mRowInflaterTaskLogger;
private final TestScope mTestScope = TestScopeProvider.getTestScope();
@@ -167,17 +163,17 @@
Context context,
TestableDependency dependency,
@Nullable TestableLooper testLooper) {
- this(context, dependency, testLooper, new FakeFeatureFlags());
+ this(context, dependency, testLooper, new FakeFeatureFlagsClassic());
}
public NotificationTestHelper(
Context context,
TestableDependency dependency,
@Nullable TestableLooper testLooper,
- @NonNull FakeFeatureFlags featureFlags) {
+ @NonNull FakeFeatureFlagsClassic featureFlags) {
mContext = context;
mFeatureFlags = Objects.requireNonNull(featureFlags);
- dependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
+ dependency.injectTestDependency(FeatureFlagsClassic.class, mFeatureFlags);
dependency.injectMockDependency(NotificationMediaManager.class);
dependency.injectMockDependency(NotificationShadeWindowController.class);
dependency.injectMockDependency(MediaOutputDialogManager.class);
@@ -280,24 +276,6 @@
}
/**
- * Creates a generic row with rounded border.
- *
- * @return a generic row with the set roundness.
- * @throws Exception
- */
- public ExpandableNotificationRow createRowWithRoundness(
- float topRoundness,
- float bottomRoundness,
- SourceType sourceType
- ) throws Exception {
- ExpandableNotificationRow row = createRow();
- row.requestRoundness(topRoundness, bottomRoundness, sourceType, /*animate = */ false);
- assertEquals(topRoundness, row.getTopRoundness(), /* delta = */ 0f);
- assertEquals(bottomRoundness, row.getBottomRoundness(), /* delta = */ 0f);
- return row;
- }
-
- /**
* Creates a generic row.
*
* @return a generic row with no special properties.
@@ -400,9 +378,8 @@
null /* groupKey */,
makeBubbleMetadata(null /* deleteIntent */, false /* autoExpand */));
n.flags |= FLAG_FSI_REQUESTED_BUT_DENIED;
- ExpandableNotificationRow row = generateRow(n, PKG, UID, USER_HANDLE,
+ return generateRow(n, PKG, UID, USER_HANDLE,
mDefaultInflationFlags, IMPORTANCE_HIGH);
- return row;
}
@@ -668,7 +645,6 @@
mStatusBarStateController,
mPeopleNotificationIdentifier,
mOnUserInteractionCallback,
- Optional.of(mock(BubblesManager.class)),
mock(NotificationGutsManager.class),
mDismissibilityProvider,
mock(MetricsLogger.class),
@@ -676,7 +652,6 @@
mock(ColorUpdateLogger.class),
mock(SmartReplyConstants.class),
mock(SmartReplyController.class),
- mFeatureFlags,
mock(IStatusBarService.class),
mock(UiEventLogger.class));
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
index 660eb30..87833d0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
@@ -5,7 +5,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.util.mockito.mock
import junit.framework.Assert.assertEquals
@@ -18,7 +18,7 @@
@RunWith(AndroidJUnit4::class)
@RunWithLooper
class NotificationTargetsHelperTest : SysuiTestCase() {
- private val featureFlags = FakeFeatureFlags()
+ private val featureFlags = FakeFeatureFlagsClassic()
lateinit var notificationTestHelper: NotificationTestHelper
private val sectionsManager: NotificationSectionsManager = mock()
private val stackScrollLayout: NotificationStackScrollLayout = mock()
@@ -90,11 +90,7 @@
)
val expected =
- RoundableTargets(
- before = children.attachedChildren[1],
- swiped = swiped,
- after = null,
- )
+ RoundableTargets(before = children.attachedChildren[1], swiped = swiped, after = null)
assertEquals(expected, actual)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index f76f1ce..7f139bd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -30,6 +30,8 @@
import android.os.PowerManager;
import android.os.UserHandle;
import android.os.Vibrator;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.view.HapticFeedbackConstants;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -45,6 +47,8 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.qs.flags.QSComposeFragment;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.CameraLauncher;
@@ -54,15 +58,14 @@
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
+import com.android.systemui.shade.shared.flag.DualShade;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import dagger.Lazy;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -72,6 +75,8 @@
import java.util.Optional;
+import dagger.Lazy;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
@@ -105,6 +110,7 @@
@Mock private ActivityStarter mActivityStarter;
@Mock private EmergencyGestureIntentFactory mEmergencyGestureIntentFactory;
@Mock private KeyguardInteractor mKeyguardInteractor;
+ @Mock private QSPanelController mQSPanelController;
CentralSurfacesCommandQueueCallbacks mSbcqCallbacks;
@@ -150,6 +156,7 @@
when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
when(mRemoteInputQuickSettingsDisabler.adjustDisableFlags(anyInt()))
.thenAnswer((Answer<Integer>) invocation -> invocation.getArgument(0));
+ when(mCentralSurfaces.getQSPanelController()).thenReturn(mQSPanelController);
}
@Test
@@ -230,4 +237,45 @@
verify(mQSHost).addTile(c, false);
}
+
+ @Test
+ @DisableFlags(value = {QSComposeFragment.FLAG_NAME, DualShade.FLAG_NAME})
+ public void clickQsTile_flagsDisabled_callsQSPanelController() {
+ ComponentName c = new ComponentName("testpkg", "testcls");
+
+ mSbcqCallbacks.clickTile(c);
+ verify(mQSPanelController).clickTile(c);
+ }
+
+ @Test
+ @DisableFlags(DualShade.FLAG_NAME)
+ @EnableFlags(QSComposeFragment.FLAG_NAME)
+ public void clickQsTile_onlyQSComposeFlag_callsQSHost() {
+ ComponentName c = new ComponentName("testpkg", "testcls");
+
+ mSbcqCallbacks.clickTile(c);
+ verify(mQSPanelController, never()).clickTile(c);
+ verify(mQSHost).clickTile(c);
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ @DisableFlags(QSComposeFragment.FLAG_NAME)
+ public void clickQsTile_onlyDualShadeFlag_callsQSHost() {
+ ComponentName c = new ComponentName("testpkg", "testcls");
+
+ mSbcqCallbacks.clickTile(c);
+ verify(mQSPanelController, never()).clickTile(c);
+ verify(mQSHost).clickTile(c);
+ }
+
+ @Test
+ @EnableFlags(value = {QSComposeFragment.FLAG_NAME, DualShade.FLAG_NAME})
+ public void clickQsTile_qsComposeAndDualShadeFlags_callsQSHost() {
+ ComponentName c = new ComponentName("testpkg", "testcls");
+
+ mSbcqCallbacks.clickTile(c);
+ verify(mQSPanelController, never()).clickTile(c);
+ verify(mQSHost).clickTile(c);
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt
index d017402..a9afb06 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt
@@ -23,24 +23,26 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.plugins.DarkIconDispatcher
-import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.plugins.fakeDarkIconDispatcher
+import com.android.systemui.plugins.statusbar.statusBarStateController
import com.android.systemui.shade.ShadeHeadsUpTracker
-import com.android.systemui.shade.ShadeViewController
-import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.shade.shadeViewController
import com.android.systemui.statusbar.HeadsUpStatusBarView
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
+import com.android.systemui.statusbar.commandQueue
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
+import com.android.systemui.statusbar.notification.domain.interactor.headsUpNotificationIconInteractor
import com.android.systemui.statusbar.notification.headsup.PinnedStatus
+import com.android.systemui.statusbar.notification.headsup.headsUpManager
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.policy.Clock
-import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import org.junit.Before
@@ -56,146 +58,141 @@
@RunWith(AndroidJUnit4::class)
@RunWithLooper
class HeadsUpAppearanceControllerTest : SysuiTestCase() {
- private val mStackScrollerController: NotificationStackScrollLayoutController =
- mock<NotificationStackScrollLayoutController>()
- private val mShadeViewController: ShadeViewController = mock<ShadeViewController>()
- private val mShadeHeadsUpTracker: ShadeHeadsUpTracker = mock<ShadeHeadsUpTracker>()
- private val mDarkIconDispatcher: DarkIconDispatcher = mock<DarkIconDispatcher>()
- private var mHeadsUpAppearanceController: HeadsUpAppearanceController? = null
- private var mTestHelper: NotificationTestHelper? = null
- private var mRow: ExpandableNotificationRow? = null
- private var mEntry: NotificationEntry? = null
- private var mHeadsUpStatusBarView: HeadsUpStatusBarView? = null
- private var mHeadsUpManager: HeadsUpManager? = null
- private var mOperatorNameView: View? = null
- private var mStatusbarStateController: StatusBarStateController? = null
- private var mPhoneStatusBarTransitions: PhoneStatusBarTransitions? = null
- private var mBypassController: KeyguardBypassController? = null
- private var mWakeUpCoordinator: NotificationWakeUpCoordinator? = null
- private var mKeyguardStateController: KeyguardStateController? = null
- private var mCommandQueue: CommandQueue? = null
- private var mNotificationRoundnessManager: NotificationRoundnessManager? = null
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+
+ private val stackScrollerController = mock<NotificationStackScrollLayoutController>()
+ private val shadeViewController = kosmos.shadeViewController
+ private val shadeHeadsUpTracker = mock<ShadeHeadsUpTracker>()
+ private val darkIconDispatcher = kosmos.fakeDarkIconDispatcher
+ private val statusBarStateController = kosmos.statusBarStateController
+ private val phoneStatusBarTransitions = kosmos.mockPhoneStatusBarTransitions
+ private val bypassController = kosmos.keyguardBypassController
+ private val wakeUpCoordinator = kosmos.notificationWakeUpCoordinator
+ private val keyguardStateController = kosmos.keyguardStateController
+ private val commandQueue = kosmos.commandQueue
+ private val notificationRoundnessManager = mock<NotificationRoundnessManager>()
+ private var headsUpManager = kosmos.headsUpManager
+
+ private lateinit var testHelper: NotificationTestHelper
+ private lateinit var row: ExpandableNotificationRow
+ private lateinit var entry: NotificationEntry
+ private lateinit var headsUpStatusBarView: HeadsUpStatusBarView
+ private lateinit var operatorNameView: View
+
+ private lateinit var underTest: HeadsUpAppearanceController
@Before
@Throws(Exception::class)
fun setUp() {
allowTestableLooperAsMainThread()
- mTestHelper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
- mRow = mTestHelper!!.createRow()
- mEntry = mRow!!.entry
- mHeadsUpStatusBarView = HeadsUpStatusBarView(mContext, mock<View>(), mock<TextView>())
- mHeadsUpManager = mock<HeadsUpManager>()
- mOperatorNameView = View(mContext)
- mStatusbarStateController = mock<StatusBarStateController>()
- mPhoneStatusBarTransitions = mock<PhoneStatusBarTransitions>()
- mBypassController = mock<KeyguardBypassController>()
- mWakeUpCoordinator = mock<NotificationWakeUpCoordinator>()
- mKeyguardStateController = mock<KeyguardStateController>()
- mCommandQueue = mock<CommandQueue>()
- mNotificationRoundnessManager = mock<NotificationRoundnessManager>()
- whenever(mShadeViewController.shadeHeadsUpTracker).thenReturn(mShadeHeadsUpTracker)
- mHeadsUpAppearanceController =
+ testHelper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
+ row = testHelper.createRow()
+ entry = row.entry
+ headsUpStatusBarView = HeadsUpStatusBarView(mContext, mock<View>(), mock<TextView>())
+ operatorNameView = View(mContext)
+
+ whenever(shadeViewController.shadeHeadsUpTracker).thenReturn(shadeHeadsUpTracker)
+ underTest =
HeadsUpAppearanceController(
- mHeadsUpManager,
- mStatusbarStateController,
- mPhoneStatusBarTransitions,
- mBypassController,
- mWakeUpCoordinator,
- mDarkIconDispatcher,
- mKeyguardStateController,
- mCommandQueue,
- mStackScrollerController,
- mShadeViewController,
- mNotificationRoundnessManager,
- mHeadsUpStatusBarView,
+ headsUpManager,
+ statusBarStateController,
+ phoneStatusBarTransitions,
+ bypassController,
+ wakeUpCoordinator,
+ darkIconDispatcher,
+ keyguardStateController,
+ commandQueue,
+ stackScrollerController,
+ shadeViewController,
+ notificationRoundnessManager,
+ headsUpStatusBarView,
Clock(mContext, null),
- mock<HeadsUpNotificationIconInteractor>(),
- Optional.of(mOperatorNameView!!),
+ kosmos.headsUpNotificationIconInteractor,
+ Optional.of(operatorNameView),
)
- mHeadsUpAppearanceController!!.setAppearFraction(0.0f, 0.0f)
+ underTest.setAppearFraction(0.0f, 0.0f)
}
@Test
fun testShowinEntryUpdated() {
- mRow!!.setPinnedStatus(PinnedStatus.PinnedBySystem)
- whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(true)
- whenever(mHeadsUpManager!!.getTopEntry()).thenReturn(mEntry)
- mHeadsUpAppearanceController!!.onHeadsUpPinned(mEntry)
- assertThat(mHeadsUpStatusBarView!!.showingEntry).isEqualTo(mRow!!.entry)
+ row.setPinnedStatus(PinnedStatus.PinnedBySystem)
+ whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(true)
+ whenever(headsUpManager.getTopEntry()).thenReturn(entry)
+ underTest.onHeadsUpPinned(entry)
+ assertThat(headsUpStatusBarView.showingEntry).isEqualTo(row.entry)
- mRow!!.setPinnedStatus(PinnedStatus.NotPinned)
- whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(false)
- mHeadsUpAppearanceController!!.onHeadsUpUnPinned(mEntry)
- assertThat(mHeadsUpStatusBarView!!.showingEntry).isNull()
+ row.setPinnedStatus(PinnedStatus.NotPinned)
+ whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(false)
+ underTest.onHeadsUpUnPinned(entry)
+ assertThat(headsUpStatusBarView.showingEntry).isNull()
}
@Test
fun testPinnedStatusUpdated() {
- mRow!!.setPinnedStatus(PinnedStatus.PinnedBySystem)
- whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(true)
- whenever(mHeadsUpManager!!.getTopEntry()).thenReturn(mEntry)
- mHeadsUpAppearanceController!!.onHeadsUpPinned(mEntry)
- assertThat(mHeadsUpAppearanceController!!.pinnedStatus)
- .isEqualTo(PinnedStatus.PinnedBySystem)
+ row.setPinnedStatus(PinnedStatus.PinnedBySystem)
+ whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(true)
+ whenever(headsUpManager.getTopEntry()).thenReturn(entry)
+ underTest.onHeadsUpPinned(entry)
+ assertThat(underTest.pinnedStatus).isEqualTo(PinnedStatus.PinnedBySystem)
- mRow!!.setPinnedStatus(PinnedStatus.NotPinned)
- whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(false)
- mHeadsUpAppearanceController!!.onHeadsUpUnPinned(mEntry)
- assertThat(mHeadsUpAppearanceController!!.pinnedStatus).isEqualTo(PinnedStatus.NotPinned)
+ row.setPinnedStatus(PinnedStatus.NotPinned)
+ whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(false)
+ underTest.onHeadsUpUnPinned(entry)
+ assertThat(underTest.pinnedStatus).isEqualTo(PinnedStatus.NotPinned)
}
@Test
@DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME)
fun testHeaderUpdated() {
- mRow!!.setPinnedStatus(PinnedStatus.PinnedBySystem)
- whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(true)
- whenever(mHeadsUpManager!!.getTopEntry()).thenReturn(mEntry)
- mHeadsUpAppearanceController!!.onHeadsUpPinned(mEntry)
- assertThat(mRow!!.headerVisibleAmount).isEqualTo(0.0f)
+ row.setPinnedStatus(PinnedStatus.PinnedBySystem)
+ whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(true)
+ whenever(headsUpManager.getTopEntry()).thenReturn(entry)
+ underTest.onHeadsUpPinned(entry)
+ assertThat(row.headerVisibleAmount).isEqualTo(0.0f)
- mRow!!.setPinnedStatus(PinnedStatus.NotPinned)
- whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(false)
- mHeadsUpAppearanceController!!.onHeadsUpUnPinned(mEntry)
- assertThat(mRow!!.headerVisibleAmount).isEqualTo(1.0f)
+ row.setPinnedStatus(PinnedStatus.NotPinned)
+ whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(false)
+ underTest.onHeadsUpUnPinned(entry)
+ assertThat(row.headerVisibleAmount).isEqualTo(1.0f)
}
@Test
fun testOperatorNameViewUpdated() {
- mHeadsUpAppearanceController!!.setAnimationsEnabled(false)
+ underTest.setAnimationsEnabled(false)
- mRow!!.setPinnedStatus(PinnedStatus.PinnedBySystem)
- whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(true)
- whenever(mHeadsUpManager!!.getTopEntry()).thenReturn(mEntry)
- mHeadsUpAppearanceController!!.onHeadsUpPinned(mEntry)
- assertThat(mOperatorNameView!!.visibility).isEqualTo(View.INVISIBLE)
+ row.setPinnedStatus(PinnedStatus.PinnedBySystem)
+ whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(true)
+ whenever(headsUpManager.getTopEntry()).thenReturn(entry)
+ underTest.onHeadsUpPinned(entry)
+ assertThat(operatorNameView.visibility).isEqualTo(View.INVISIBLE)
- mRow!!.setPinnedStatus(PinnedStatus.NotPinned)
- whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(false)
- mHeadsUpAppearanceController!!.onHeadsUpUnPinned(mEntry)
- assertThat(mOperatorNameView!!.visibility).isEqualTo(View.VISIBLE)
+ row.setPinnedStatus(PinnedStatus.NotPinned)
+ whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(false)
+ underTest.onHeadsUpUnPinned(entry)
+ assertThat(operatorNameView.visibility).isEqualTo(View.VISIBLE)
}
@Test
fun constructor_animationValuesUpdated() {
val appearFraction = .75f
val expandedHeight = 400f
- whenever(mStackScrollerController.appearFraction).thenReturn(appearFraction)
- whenever(mStackScrollerController.expandedHeight).thenReturn(expandedHeight)
+ whenever(stackScrollerController.appearFraction).thenReturn(appearFraction)
+ whenever(stackScrollerController.expandedHeight).thenReturn(expandedHeight)
val newController =
HeadsUpAppearanceController(
- mHeadsUpManager,
- mStatusbarStateController,
- mPhoneStatusBarTransitions,
- mBypassController,
- mWakeUpCoordinator,
- mDarkIconDispatcher,
- mKeyguardStateController,
- mCommandQueue,
- mStackScrollerController,
- mShadeViewController,
- mNotificationRoundnessManager,
- mHeadsUpStatusBarView,
+ headsUpManager,
+ statusBarStateController,
+ phoneStatusBarTransitions,
+ bypassController,
+ wakeUpCoordinator,
+ darkIconDispatcher,
+ keyguardStateController,
+ commandQueue,
+ stackScrollerController,
+ shadeViewController,
+ notificationRoundnessManager,
+ headsUpStatusBarView,
Clock(mContext, null),
mock<HeadsUpNotificationIconInteractor>(),
Optional.empty(),
@@ -207,77 +204,76 @@
@Test
fun testDestroy() {
- reset(mHeadsUpManager)
- reset(mDarkIconDispatcher)
- reset(mShadeHeadsUpTracker)
- reset(mStackScrollerController)
+ reset(headsUpManager)
+ reset(shadeHeadsUpTracker)
+ reset(stackScrollerController)
- mHeadsUpAppearanceController!!.onViewDetached()
+ underTest.onViewDetached()
- verify(mHeadsUpManager!!).removeListener(any())
- verify(mDarkIconDispatcher).removeDarkReceiver(any())
- verify(mShadeHeadsUpTracker).removeTrackingHeadsUpListener(any())
- verify(mShadeHeadsUpTracker).setHeadsUpAppearanceController(null)
- verify(mStackScrollerController).removeOnExpandedHeightChangedListener(any())
+ verify(headsUpManager).removeListener(any())
+ assertThat(darkIconDispatcher.receivers).isEmpty()
+ verify(shadeHeadsUpTracker).removeTrackingHeadsUpListener(any())
+ verify(shadeHeadsUpTracker).setHeadsUpAppearanceController(null)
+ verify(stackScrollerController).removeOnExpandedHeightChangedListener(any())
}
@Test
fun testPulsingRoundness_onUpdateHeadsUpAndPulsingRoundness() {
// Pulsing: Enable flag and dozing
- whenever(mNotificationRoundnessManager!!.shouldRoundNotificationPulsing()).thenReturn(true)
- whenever(mTestHelper!!.statusBarStateController.isDozing).thenReturn(true)
+ whenever(notificationRoundnessManager.shouldRoundNotificationPulsing()).thenReturn(true)
+ whenever(testHelper.statusBarStateController.isDozing).thenReturn(true)
// Pulsing: Enabled
- mRow!!.isHeadsUp = true
- mHeadsUpAppearanceController!!.updateHeadsUpAndPulsingRoundness(mEntry)
+ row.isHeadsUp = true
+ underTest.updateHeadsUpAndPulsingRoundness(entry)
- val debugString: String = mRow!!.roundableState.debugString()
+ val debugString: String = row.roundableState.debugString()
// If Pulsing is enabled, roundness should be set to 1
- assertThat(mRow!!.topRoundness.toDouble()).isWithin(0.001).of(1.0)
+ assertThat(row.topRoundness.toDouble()).isWithin(0.001).of(1.0)
assertThat(debugString).contains("Pulsing")
// Pulsing: Disabled
- mRow!!.isHeadsUp = false
- mHeadsUpAppearanceController!!.updateHeadsUpAndPulsingRoundness(mEntry)
+ row.isHeadsUp = false
+ underTest.updateHeadsUpAndPulsingRoundness(entry)
// If Pulsing is disabled, roundness should be set to 0
- assertThat(mRow!!.topRoundness.toDouble()).isWithin(0.001).of(0.0)
+ assertThat(row.topRoundness.toDouble()).isWithin(0.001).of(0.0)
}
@Test
fun testPulsingRoundness_onHeadsUpStateChanged() {
// Pulsing: Enable flag and dozing
- whenever(mNotificationRoundnessManager!!.shouldRoundNotificationPulsing()).thenReturn(true)
- whenever(mTestHelper!!.statusBarStateController.isDozing).thenReturn(true)
+ whenever(notificationRoundnessManager.shouldRoundNotificationPulsing()).thenReturn(true)
+ whenever(testHelper.statusBarStateController.isDozing).thenReturn(true)
// Pulsing: Enabled
- mEntry!!.setHeadsUp(true)
- mHeadsUpAppearanceController!!.onHeadsUpStateChanged(mEntry!!, true)
+ entry.setHeadsUp(true)
+ underTest.onHeadsUpStateChanged(entry, true)
- val debugString: String = mRow!!.roundableState.debugString()
+ val debugString: String = row.roundableState.debugString()
// If Pulsing is enabled, roundness should be set to 1
- assertThat(mRow!!.topRoundness.toDouble()).isWithin(0.001).of(1.0)
+ assertThat(row.topRoundness.toDouble()).isWithin(0.001).of(1.0)
assertThat(debugString).contains("Pulsing")
// Pulsing: Disabled
- mEntry!!.setHeadsUp(false)
- mHeadsUpAppearanceController!!.onHeadsUpStateChanged(mEntry!!, false)
+ entry.setHeadsUp(false)
+ underTest.onHeadsUpStateChanged(entry, false)
// If Pulsing is disabled, roundness should be set to 0
- assertThat(mRow!!.topRoundness.toDouble()).isWithin(0.001).of(0.0)
+ assertThat(row.topRoundness.toDouble()).isWithin(0.001).of(0.0)
}
@Test
fun onHeadsUpStateChanged_true_transitionsNotified() {
- mHeadsUpAppearanceController!!.onHeadsUpStateChanged(mEntry!!, true)
+ underTest.onHeadsUpStateChanged(entry, true)
- verify(mPhoneStatusBarTransitions!!).onHeadsUpStateChanged(true)
+ verify(phoneStatusBarTransitions).onHeadsUpStateChanged(true)
}
@Test
fun onHeadsUpStateChanged_false_transitionsNotified() {
- mHeadsUpAppearanceController!!.onHeadsUpStateChanged(mEntry!!, false)
+ underTest.onHeadsUpStateChanged(entry, false)
- verify(mPhoneStatusBarTransitions!!).onHeadsUpStateChanged(false)
+ verify(phoneStatusBarTransitions).onHeadsUpStateChanged(false)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
index c0a206a..9ad2315 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
@@ -49,11 +49,7 @@
private val dispatcher = StandardTestDispatcher()
private val testScope = TestScope(dispatcher)
- private val iconsInteractor =
- FakeMobileIconsInteractor(
- FakeMobileMappingsProxy(),
- mock(),
- )
+ private val iconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
private val repo = FakeDeviceBasedSatelliteRepository()
private val connectivityRepository = FakeConnectivityRepository()
@@ -515,7 +511,7 @@
// GIVEN, 2 connection
val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
- val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+ val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
// WHEN all connections are NOT OOS.
i1.isInService.value = true
@@ -547,7 +543,7 @@
// GIVEN a condition that should return true (all conections OOS)
val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
- val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+ val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
i1.isInService.value = true
i2.isInService.value = true
@@ -579,4 +575,40 @@
// THEN the interactor returns true due to the wifi network being active
assertThat(latest).isTrue()
}
+
+ @Test
+ @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ fun isAnyConnectionNtn_trueWhenAnyNtn() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isAnyConnectionNtn)
+
+ // GIVEN, 2 connection
+ val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+ val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
+
+ // WHEN at least one connection is using ntn
+ i1.isNonTerrestrial.value = true
+ i2.isNonTerrestrial.value = false
+
+ // THEN the value is propagated to this interactor
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ fun isAnyConnectionNtn_falseWhenNoNtn() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isAnyConnectionNtn)
+
+ // GIVEN, 2 connection
+ val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+ val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
+
+ // WHEN at no connection is using ntn
+ i1.isNonTerrestrial.value = false
+ i2.isNonTerrestrial.value = false
+
+ // THEN the value is propagated to this interactor
+ assertThat(latest).isFalse()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
index 509aa7a..fe5b56a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
@@ -327,10 +327,11 @@
// GIVEN satellite is allowed
repo.isSatelliteAllowedForCurrentLocation.value = true
- // GIVEN all icons are OOS
+ // GIVEN all icons are OOS and not ntn
val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
i1.isInService.value = false
i1.isEmergencyOnly.value = false
+ i1.isNonTerrestrial.value = false
// GIVEN apm is disabled
airplaneModeRepository.setIsAirplaneMode(false)
@@ -344,6 +345,29 @@
}
@Test
+ fun icon_nullWhenConnected_mobileNtnConnectionExists() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.icon)
+
+ // GIVEN satellite is allowed
+ repo.isSatelliteAllowedForCurrentLocation.value = true
+
+ // GIVEN ntn connection exists
+ val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
+ i1.isNonTerrestrial.value = true
+
+ // GIVEN apm is disabled
+ airplaneModeRepository.setIsAirplaneMode(false)
+
+ // GIVEN satellite reports that it is Connected
+ repo.connectionState.value = SatelliteConnectionState.On
+
+ // THEN icon is null because despite being connected, the mobile stack is reporting a
+ // nonTerrestrial network, and therefore will have its own icon
+ assertThat(latest).isNull()
+ }
+
+ @Test
fun icon_satelliteIsProvisioned() =
testScope.runTest {
val latest by collectLastValue(underTest.icon)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt
index 9888574..a2ca12c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt
@@ -30,6 +30,7 @@
var listener: StatusBarVisibilityChangeListener? = null
override fun bind(
+ displayId: Int,
view: View,
viewModel: HomeStatusBarViewModel,
systemEventChipAnimateIn: ((View) -> Unit)?,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
index 4e33a59..3d6882c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
@@ -251,6 +251,22 @@
}
@Test
+ fun deactivateAllModes_updatesCorrectModes() =
+ testScope.runTest {
+ zenModeRepository.addModes(
+ listOf(
+ TestModeBuilder.MANUAL_DND_ACTIVE,
+ TestModeBuilder().setName("Inactive").setActive(false).build(),
+ TestModeBuilder().setName("Active").setActive(true).build(),
+ )
+ )
+
+ underTest.deactivateAllModes()
+
+ assertThat(zenModeRepository.getModes().filter { it.isActive }).isEmpty()
+ }
+
+ @Test
fun activeModes_computesMainActiveMode() =
testScope.runTest {
val activeModes by collectLastValue(underTest.activeModes)
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index dcb15a7..b2083c2 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -213,6 +213,13 @@
default int getCurrentCardTopPadding() {
throw new UnsupportedOperationException("Not implemented by " + getClass());
}
+
+ /**
+ * Set the horizontal paddings for applicable children inside the SmartspaceView.
+ */
+ default void setHorizontalPaddings(int horizontalPadding) {
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
+ }
}
/** Interface for launching Intents, which can differ on the lockscreen */
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index e3cbd66..322da32 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -14,7 +14,6 @@
package com.android.systemui.plugins.qs;
-import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
@@ -22,6 +21,7 @@
import android.service.quicksettings.Tile;
import android.text.TextUtils;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.logging.InstanceId;
@@ -92,6 +92,7 @@
CharSequence getTileLabel();
+ @NonNull
State getState();
default LogMaker populate(LogMaker logMaker) {
@@ -269,6 +270,7 @@
return sb.append(']');
}
+ @NonNull
public State copy() {
State state = new State();
copyTo(state);
@@ -304,6 +306,7 @@
return rt;
}
+ @androidx.annotation.NonNull
@Override
public State copy() {
AdapterState state = new AdapterState();
@@ -316,6 +319,7 @@
class BooleanState extends AdapterState {
public static final int VERSION = 1;
+ @androidx.annotation.NonNull
@Override
public State copy() {
BooleanState state = new BooleanState();
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index ba0d7de..bfb37a0d 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -110,6 +110,7 @@
<dimen name="below_clock_padding_start">32dp</dimen>
<dimen name="below_clock_padding_end">16dp</dimen>
<dimen name="below_clock_padding_start_icons">28dp</dimen>
+ <dimen name="smartspace_padding_horizontal">16dp</dimen>
<!-- Proportion of the screen height to use to set the maximum height of the bouncer to when
the device is in the DEVICE_POSTURE_HALF_OPENED posture, for the PIN/pattern entry. 0 will
diff --git a/packages/SystemUI/res/drawable/ic_arrow_down_24dp.xml b/packages/SystemUI/res/drawable/ic_arrow_down_24dp.xml
new file mode 100644
index 0000000..0640116
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_arrow_down_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960">
+ <path
+ android:fillColor="#000000"
+ android:pathData="M480,616 L240,376l56,-56 184,184 184,-184 56,56 -240,240Z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_arrow_up_24dp.xml b/packages/SystemUI/res/drawable/ic_arrow_up_24dp.xml
new file mode 100644
index 0000000..65a3eef
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_arrow_up_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960">
+ <path
+ android:fillColor="#000000"
+ android:pathData="M480,432 L296,616l-56,-56 240,-240 240,240 -56,56 -184,-184Z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_screensaver_auto.xml b/packages/SystemUI/res/drawable/ic_screensaver_auto.xml
new file mode 100644
index 0000000..7cccff6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_screensaver_auto.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.
+ -->
+
+<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="M160,240Q160,240 160,240Q160,240 160,240L160,720Q160,720 160,720Q160,720 160,720L160,720Q160,720 160,720Q160,720 160,720L160,240Q160,240 160,240Q160,240 160,240L160,240Q160,240 160,240Q160,240 160,240Q160,240 160,240Q160,240 160,240ZM160,800Q127,800 103.5,776.5Q80,753 80,720L80,240Q80,207 103.5,183.5Q127,160 160,160L440,160Q440,179 440,199Q440,219 440,240L160,240Q160,240 160,240Q160,240 160,240L160,720Q160,720 160,720Q160,720 160,720L800,720Q800,720 800,720Q800,720 800,720L800,480Q823,480 843,480Q863,480 880,480L880,720Q880,753 856.5,776.5Q833,800 800,800L160,800ZM700,480Q700,388 636,324Q572,260 480,260Q572,260 636,196Q700,132 700,40Q700,132 764,196Q828,260 920,260Q828,260 764,324Q700,388 700,480Z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index 2b40df4..5922a7d 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -149,7 +149,8 @@
android:clickable="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="start|center_vertical">
+ android:minHeight="72dp"
+ android:gravity="center_vertical">
<TextView
android:id="@+id/mobile_title"
android:maxLines="1"
diff --git a/packages/SystemUI/res/layout/screen_record_options.xml b/packages/SystemUI/res/layout/screen_record_options.xml
index 4b5cdb5..c6a9470 100644
--- a/packages/SystemUI/res/layout/screen_record_options.xml
+++ b/packages/SystemUI/res/layout/screen_record_options.xml
@@ -29,7 +29,7 @@
android:tint="?android:attr/textColorSecondary"
android:layout_gravity="center_vertical"
android:layout_weight="0"
- android:layout_marginRight="@dimen/screenrecord_option_padding"
+ android:layout_marginEnd="@dimen/screenrecord_option_padding"
android:importantForAccessibility="no"/>
<Spinner
android:id="@+id/screen_recording_options"
@@ -63,7 +63,7 @@
android:layout_height="@dimen/screenrecord_option_icon_size"
android:src="@drawable/ic_touch"
android:tint="?android:attr/textColorSecondary"
- android:layout_marginRight="@dimen/screenrecord_option_padding"
+ android:layout_marginEnd="@dimen/screenrecord_option_padding"
android:importantForAccessibility="no"/>
<TextView
android:layout_width="0dp"
diff --git a/packages/SystemUI/res/layout/volume_dialog_slider.xml b/packages/SystemUI/res/layout/volume_dialog_slider.xml
index c1852b1..9ac456c 100644
--- a/packages/SystemUI/res/layout/volume_dialog_slider.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_slider.xml
@@ -14,15 +14,21 @@
limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/volume_dialog_slider_width"
- android:layout_height="@dimen/volume_dialog_slider_height">
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
<com.google.android.material.slider.Slider
- style="@style/SystemUI.Material3.Slider.Volume"
android:id="@+id/volume_dialog_slider"
- android:layout_width="@dimen/volume_dialog_slider_height"
- android:layout_height="match_parent"
+ style="@style/SystemUI.Material3.Slider.Volume"
+ android:layout_width="@dimen/volume_dialog_slider_width"
+ android:layout_height="@dimen/volume_dialog_slider_height"
android:layout_gravity="center"
- android:rotation="270"
- android:theme="@style/Theme.Material3.Light" />
-</FrameLayout>
+ android:theme="@style/Theme.Material3.Light"
+ android:orientation="vertical"
+ app:thumbHeight="52dp"
+ app:trackCornerSize="12dp"
+ app:trackHeight="40dp"
+ app:trackStopIndicatorSize="6dp"
+ app:trackInsideCornerSize="2dp" />
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/trackpad_recent_apps_edu.json b/packages/SystemUI/res/raw/trackpad_recent_apps_edu.json
index 10768fc..2866266 100644
--- a/packages/SystemUI/res/raw/trackpad_recent_apps_edu.json
+++ b/packages/SystemUI/res/raw/trackpad_recent_apps_edu.json
@@ -1 +1 @@
-{"v":"5.12.1","fr":60,"ip":0,"op":511,"w":554,"h":564,"nm":"Trackpad-JSON_Recents-EDU","ddd":0,"assets":[{"id":"comp_0","nm":"Recents_EDU Loop","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"CNTL || playback","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":0,"ix":3},"y":{"a":0,"k":0,"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Picker","np":3,"mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ","ix":1,"en":1,"ef":[{"ty":7,"nm":"Menu","mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ-0001","ix":1,"v":{"a":0,"k":2,"ix":1}}]},{"ty":5,"nm":"OUTPUT","np":3,"mn":"ADBE Slider Control","ix":2,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"k":[{"s":[0],"t":142,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.001],"t":143,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.002],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.003],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.004],"t":146,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.006],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.008],"t":148,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.01],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.012],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.016],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.02],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.025],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.031],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.038],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.047],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.059],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.073],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.091],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.116],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.15],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.196],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.249],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.306],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.366],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.425],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.481],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.53],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.575],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.614],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.648],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.678],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.706],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.73],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.752],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.772],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.79],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.807],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.822],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.836],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.849],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.861],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.873],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.883],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.892],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.901],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.91],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.917],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.925],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.931],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.937],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.943],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.949],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.954],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.958],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.963],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.967],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.97],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.974],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.977],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.98],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.983],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.985],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.987],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.989],"t":205,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.991],"t":206,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.993],"t":207,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.994],"t":208,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.996],"t":209,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.997],"t":210,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.998],"t":211,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.998],"t":212,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.999],"t":213,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1],"t":215,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.009],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.038],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.093],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.193],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.4],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.636],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.739],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.8],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.84],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.871],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.894],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.912],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.928],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.94],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.951],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.959],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.967],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.973],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.979],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.983],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.987],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.99],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.993],"t":273,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.995],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.997],"t":275,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.998],"t":276,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.999],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2],"t":380,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.009],"t":381,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.038],"t":382,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.093],"t":383,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.193],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.4],"t":385,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.636],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.739],"t":387,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.8],"t":388,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.84],"t":389,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.871],"t":390,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.894],"t":391,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.912],"t":392,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.928],"t":393,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.94],"t":394,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.951],"t":395,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.959],"t":396,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.967],"t":397,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.973],"t":398,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.979],"t":399,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.983],"t":400,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.987],"t":401,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.99],"t":402,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.993],"t":403,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.995],"t":404,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.997],"t":405,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.999],"t":408,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}}]},{"ty":5,"nm":"Keys","np":3,"mn":"ADBE Slider Control","ix":3,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.831],"y":[0.109]},"o":{"x":[0.458],"y":[0.053]},"t":142,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.15],"y":[0.43]},"t":161,"s":[0.15]},{"t":217,"s":[1],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[1]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[1.4]},{"t":280,"s":[2],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[2]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":385,"s":[2.4]},{"t":410,"s":[3]}],"ix":1}}]},{"ty":5,"nm":"State (holds)","np":3,"mn":"ADBE Slider Control","ix":4,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":0,"k":0,"ix":1}}]}],"shapes":[],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"Null :: Taskbar drop","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[252,278,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,278.45,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,279.615,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,281.252,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,283.166,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,287.233,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,289.181,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,290.982,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,292.599,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,294.012,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,295.216,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,296.216,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,297.023,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,297.655,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.131,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.474,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.705,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.465,0],"t":212,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.226,0],"t":215,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298,0],"t":377,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.382,0],"t":378,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,299.372,0],"t":379,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,300.764,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,302.391,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,305.848,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,307.504,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,309.035,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,310.409,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,311.611,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,312.634,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,313.483,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,314.169,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,314.706,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.112,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.403,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.717,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.474,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.192,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"Taskbar Lofi","parent":3,"refId":"comp_1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":134,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":143,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":432,"s":[100]},{"t":444,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":0,"ix":3},"y":{"k":[{"s":[26.984],"t":127,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.971],"t":128,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.95],"t":129,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.921],"t":130,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.882],"t":131,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.83],"t":132,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.765],"t":133,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.685],"t":134,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.589],"t":135,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.478],"t":136,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.349],"t":137,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.205],"t":138,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.072],"t":139,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.926],"t":140,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.764],"t":141,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.589],"t":142,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.397],"t":143,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.187],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.959],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.711],"t":146,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.44],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.146],"t":148,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.826],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.479],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.1],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.686],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.236],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[21.745],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[21.207],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[20.616],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.967],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.248],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.457],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.578],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.602],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.514],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.303],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[12.954],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[11.477],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[9.885],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[8.215],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[6.526],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[4.878],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[3.338],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.928],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.659],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.475],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-1.485],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-2.388],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-3.192],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-3.911],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-4.556],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-5.136],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-5.662],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-6.135],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-6.563],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-6.951],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-7.303],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-7.622],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-7.913],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.175],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.413],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.628],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.823],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.998],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.155],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.296],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.42],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.531],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.627],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.711],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.783],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.843],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.893],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.933],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.963],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.984],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.996],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[91,15,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}],"ix":1}}]}],"w":182,"h":30,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":3,"nm":"Focus Task :: Lift & Drop","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":252,"ix":3},"y":{"k":[{"s":[157.385],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.28],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.128],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.026],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.901],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.75],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.564],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.335],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.054],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.706],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.275],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[154.73],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[154.03],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[153.103],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[151.8],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[150.035],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[148.047],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.867],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[143.589],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[141.341],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[139.241],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[137.346],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[135.666],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[134.185],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[132.878],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[131.718],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[130.684],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[129.755],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[128.916],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[128.155],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[127.462],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[126.829],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[126.249],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[125.715],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[125.221],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.765],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.343],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[123.951],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[123.587],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[123.249],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.934],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.641],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.369],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.114],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.877],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.657],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.452],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.26],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.082],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.918],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.764],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.623],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.492],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.371],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.261],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.158],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.065],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.98],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.903],"t":205,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.835],"t":206,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.718],"t":208,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.629],"t":210,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.51],"t":215,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.5],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.746],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.54],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.071],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.808],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[130.5],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[136.982],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[139.835],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[141.489],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[142.613],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[143.442],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.082],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.593],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.01],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.354],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.642],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.884],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.089],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.262],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.409],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.534],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.638],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.725],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.857],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147],"t":380,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147.094],"t":381,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147.397],"t":382,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147.982],"t":383,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[149.027],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[151.2],"t":385,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[153.675],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[154.764],"t":387,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.396],"t":388,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.825],"t":389,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.141],"t":390,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.386],"t":391,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.581],"t":392,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.74],"t":393,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.871],"t":394,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.981],"t":395,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.074],"t":396,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.218],"t":398,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.362],"t":401,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"matte","parent":5,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"k":[{"s":[503.613,314.758],"t":144,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[503.134,314.459],"t":146,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.832,314.27],"t":147,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.464,314.04],"t":148,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.025,313.765],"t":149,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[501.487,313.429],"t":150,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[500.824,313.015],"t":151,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[500.023,312.514],"t":152,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[499.032,311.895],"t":153,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[497.818,311.136],"t":154,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[496.328,310.205],"t":155,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[494.484,309.053],"t":156,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[492.194,307.621],"t":157,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[489.307,305.817],"t":158,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[485.592,303.495],"t":159,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[480.67,300.419],"t":160,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[473.76,296.1],"t":161,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[464.395,290.247],"t":162,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[453.849,283.656],"t":163,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[442.286,276.429],"t":164,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[430.198,268.874],"t":165,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[418.274,261.421],"t":166,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[407.131,254.457],"t":167,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[397.077,248.173],"t":168,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[388.165,242.603],"t":169,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[380.31,237.694],"t":170,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[373.373,233.358],"t":171,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[367.218,229.511],"t":172,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[361.732,226.082],"t":173,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[356.803,223.002],"t":174,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[352.354,220.221],"t":175,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[348.318,217.699],"t":176,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[344.643,215.402],"t":177,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[341.283,213.302],"t":178,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[338.205,211.378],"t":179,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[335.37,209.606],"t":180,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[332.752,207.97],"t":181,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[330.33,206.456],"t":182,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[328.092,205.058],"t":183,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[326.012,203.757],"t":184,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[324.082,202.552],"t":185,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[322.291,201.432],"t":186,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[320.617,200.386],"t":187,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[319.062,199.414],"t":188,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[317.618,198.512],"t":189,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[316.267,197.667],"t":190,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[315.013,196.883],"t":191,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[313.845,196.153],"t":192,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[312.756,195.472],"t":193,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[311.738,194.837],"t":194,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[310.793,194.246],"t":195,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[309.921,193.7],"t":196,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[309.107,193.192],"t":197,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[308.359,192.724],"t":198,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[307.663,192.289],"t":199,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[307.02,191.888],"t":200,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[306.436,191.522],"t":201,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[305.891,191.182],"t":202,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[305.399,190.874],"t":203,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[304.946,190.591],"t":204,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[304.539,190.337],"t":205,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[304.178,190.112],"t":206,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.85,189.906],"t":207,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.555,189.722],"t":208,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.306,189.566],"t":209,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.082,189.427],"t":210,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.892,189.308],"t":211,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.617,189.135],"t":213,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.4,189],"t":250,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.247,188.904],"t":251,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[301.752,188.595],"t":252,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[300.798,187.999],"t":253,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[299.093,186.933],"t":254,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[295.546,184.716],"t":255,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[291.506,182.192],"t":256,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[289.729,181.08],"t":257,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[288.698,180.436],"t":258,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.998,179.999],"t":259,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.481,179.676],"t":260,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.082,179.427],"t":261,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.764,179.227],"t":262,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.504,179.065],"t":263,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.29,178.931],"t":264,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.11,178.819],"t":265,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.832,178.645],"t":267,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.555,178.472],"t":270,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.272,178.295],"t":278,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.264,178.29],"t":380,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.222,179.514],"t":381,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[293.538,183.461],"t":382,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[305.714,191.071],"t":383,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[327.48,204.675],"t":384,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[372.758,232.974],"t":385,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[424.317,265.198],"t":386,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[447.009,279.381],"t":387,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[460.167,287.605],"t":388,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[469.103,293.19],"t":389,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[475.697,297.31],"t":390,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[480.788,300.492],"t":391,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[484.853,303.033],"t":392,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[488.172,305.107],"t":393,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[490.906,306.816],"t":394,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[493.198,308.249],"t":395,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[495.121,309.451],"t":396,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[496.752,310.47],"t":397,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[498.133,311.333],"t":398,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[499.301,312.063],"t":399,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[500.29,312.681],"t":400,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[501.123,313.202],"t":401,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[501.814,313.634],"t":402,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.391,313.994],"t":403,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.861,314.288],"t":404,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[503.238,314.524],"t":405,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[503.53,314.706],"t":406,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0],"ix":3},"r":{"k":[{"s":[27.974],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.959],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.942],"t":146,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.922],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.898],"t":148,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.869],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.833],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.789],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.736],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.67],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.589],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.49],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.368],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.216],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.024],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.777],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.45],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.991],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.37],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.669],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.901],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.098],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.306],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[21.566],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[20.898],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[20.306],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.785],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.324],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.915],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.551],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.223],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.928],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.66],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.416],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.193],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.988],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.8],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.626],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.465],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.316],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.178],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.05],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.931],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.82],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.717],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.621],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.531],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.448],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.37],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.298],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.23],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.167],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.11],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.055],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.006],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.96],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.917],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.878],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.842],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.809],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.779],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.752],"t":205,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.728],"t":206,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.706],"t":207,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.687],"t":208,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.67],"t":209,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.655],"t":210,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.643],"t":211,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.633],"t":212,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.624],"t":213,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.61],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.603],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.579],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.532],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.45],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.278],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.082],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.996],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.946],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.912],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.887],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.868],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.853],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.84],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.83],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.821],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.808],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.794],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.78],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.78],"t":380,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.907],"t":381,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.318],"t":382,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.109],"t":383,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.524],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.468],"t":385,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.82],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.295],"t":387,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.15],"t":388,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.731],"t":389,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.16],"t":390,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.491],"t":391,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.755],"t":392,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.971],"t":393,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.149],"t":394,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.298],"t":395,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.423],"t":396,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.529],"t":397,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.619],"t":398,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.694],"t":399,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.759],"t":400,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.813],"t":401,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.858],"t":402,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.895],"t":403,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.926],"t":404,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.95],"t":405,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.969],"t":406,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.993],"t":408,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Recents_LofiApp","parent":6,"tt":1,"tp":6,"refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"k":[{"s":[99.923,99.923,100],"t":144,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.828,99.828,100],"t":146,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.768,99.768,100],"t":147,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.695,99.695,100],"t":148,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.608,99.608,100],"t":149,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.501,99.501,100],"t":150,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.37,99.37,100],"t":151,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.211,99.211,100],"t":152,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.014,99.014,100],"t":153,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.773,98.773,100],"t":154,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.478,98.478,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.112,98.112,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.658,97.658,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.085,97.085,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.348,96.348,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.371,95.371,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94,94,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.142,92.142,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.049,90.049,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[87.755,87.755,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.357,85.357,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.991,82.991,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.78,80.78,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[78.785,78.785,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[77.017,77.017,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[75.458,75.458,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[74.082,74.082,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[72.861,72.861,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[71.772,71.772,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70.794,70.794,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[69.911,69.911,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[69.111,69.111,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.382,68.382,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[67.715,67.715,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[67.104,67.104,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[66.542,66.542,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[66.022,66.022,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[65.542,65.542,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[65.098,65.098,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[64.685,64.685,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[64.302,64.302,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.947,63.947,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.615,63.615,100],"t":187,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.306,63.306,100],"t":188,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.02,63.02,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.751,62.751,100],"t":190,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.503,62.503,100],"t":191,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.271,62.271,100],"t":192,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.055,62.055,100],"t":193,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.853,61.853,100],"t":194,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.665,61.665,100],"t":195,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.492,61.492,100],"t":196,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.331,61.331,100],"t":197,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.182,61.182,100],"t":198,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.044,61.044,100],"t":199,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.917,60.917,100],"t":200,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.801,60.801,100],"t":201,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.693,60.693,100],"t":202,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.595,60.595,100],"t":203,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.505,60.505,100],"t":204,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.424,60.424,100],"t":205,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.353,60.353,100],"t":206,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.288,60.288,100],"t":207,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.229,60.229,100],"t":208,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.18,60.18,100],"t":209,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.135,60.135,100],"t":210,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.098,60.098,100],"t":211,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.043,60.043,100],"t":213,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60,60,100],"t":250,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.97,59.97,100],"t":251,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.871,59.871,100],"t":252,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.682,59.682,100],"t":253,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.344,59.344,100],"t":254,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[58.64,58.64,100],"t":255,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.839,57.839,100],"t":256,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.486,57.486,100],"t":257,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.281,57.281,100],"t":258,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.142,57.142,100],"t":259,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.04,57.04,100],"t":260,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.961,56.961,100],"t":261,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.898,56.898,100],"t":262,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.846,56.846,100],"t":263,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.804,56.804,100],"t":264,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.768,56.768,100],"t":265,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.713,56.713,100],"t":267,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.658,56.658,100],"t":270,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.602,56.602,100],"t":278,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.6,56.6,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.989,56.989,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[58.242,58.242,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.657,60.657,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[64.976,64.976,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[73.96,73.96,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.19,84.19,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.692,88.692,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[91.303,91.303,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.076,93.076,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.384,94.384,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.394,95.394,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.201,96.201,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.859,96.859,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.402,97.402,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.857,97.857,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.238,98.238,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.562,98.562,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.836,98.836,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.068,99.068,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.264,99.264,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.429,99.429,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.566,99.566,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.681,99.681,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.774,99.774,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.849,99.849,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.907,99.907,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":7,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"t":277,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,-30.035,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[176.678,176.678,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"second Tasks Zoom back","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":385,"s":[98,98,100]},{"t":410,"s":[95,95,100]}],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Null :: Reposition Side Task","parent":9,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-318.4,-38,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-277.34,-48.1,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,-63.25,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":12,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-111.72,0],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-111.197,0],"t":251,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-109.514,0],"t":252,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-106.268,0],"t":253,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-100.462,0],"t":254,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-88.39,0],"t":255,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-74.643,0],"t":256,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-68.591,0],"t":257,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-65.083,0],"t":258,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-62.7,0],"t":259,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-60.943,0],"t":260,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-59.584,0],"t":261,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-58.5,0],"t":262,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-57.616,0],"t":263,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.886,0],"t":264,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.276,0],"t":265,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.762,0],"t":266,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.328,0],"t":267,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.96,0],"t":268,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.648,0],"t":269,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.385,0],"t":270,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.163,0],"t":271,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.977,0],"t":272,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.824,0],"t":273,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.698,0],"t":274,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.598,0],"t":275,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.521,0],"t":276,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.463,0],"t":277,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.424,0],"t":278,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":10,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.963},"t":217,"s":[-84.8,0,0],"to":[0,0,0],"ti":[0,0,0]},{"t":250,"s":[0,0,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":250,"s":[302.4,189]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":255,"s":[227.56,142.34]},{"t":280,"s":[115.3,72.35]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[14.6]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[14.272]},{"t":280,"s":[13.78]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":14,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-53.175,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.175,0],"t":510,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-411.95,20.325,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-333.47,29.183,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,42.47,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[115.3,72.35],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13.78,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Taskbar Lofi","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"app - 5","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[51.5,0,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.652,0,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.136,0,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[53.013,0,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[54.449,0,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.806,0,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[61.3,0,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[66.437,0,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[68.94,0,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[70.432,0,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[71.462,0,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[72.229,0,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[72.83,0,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[73.314,0,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[73.714,0,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.048,0,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.334,0,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.578,0,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.789,0,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.971,0,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.131,0,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.269,0,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.389,0,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.493,0,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.584,0,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.663,0,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.731,0,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.789,0,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.839,0,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.915,0,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.982,0,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[76,0,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.779,0,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.066,0,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[73.709,0,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[71.271,0,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[66.2,0,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[60.425,0,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[57.886,0,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.41,0,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.409,0,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[54.67,0,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[54.1,0,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[53.646,0,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[53.275,0,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.968,0,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.711,0,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.495,0,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.312,0,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.157,0,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.026,0,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.916,0,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.822,0,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.745,0,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.68,0,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.628,0,0],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.585,0,0],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.552,0,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.501,0,0],"t":409,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[167,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7511","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[167,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 5","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"app - 4","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[123.341,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.654,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.223,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[125.146,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[126.662,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[129.549,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[132.838,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[134.455,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[135.421,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[136.083,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[136.576,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[136.962,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.272,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.528,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.742,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.924,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.08,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.216,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.334,15,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.437,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.527,15,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.606,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.734,15,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.869,15,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.864,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.402,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.527,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[135.96,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[132.701,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[129.002,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[127.358,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[126.406,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[125.763,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[125.288,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.923,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.633,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.396,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.199,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.034,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.895,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.776,15,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.675,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.589,15,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.516,15,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.403,15,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.299,15,0],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[139,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7508","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[139,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"app - 3","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[104.041,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.182,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.436,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.844,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.517,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[106.808,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[108.265,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[108.981,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.409,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.703,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.923,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.092,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.228,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.341,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.436,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.517,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.587,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.648,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.746,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.853,15,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.956,15,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.938,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.73,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.34,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.649,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[108.197,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[106.559,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.828,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.403,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.117,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.906,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.745,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.616,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.511,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.424,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.35,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.288,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.19,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.091,15,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[111,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7507","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[111,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 3","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"app - 2","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[84.704,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.639,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.537,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.371,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.048,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.684,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.505,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.398,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.324,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.271,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.195,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.123,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.045,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.068,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.166,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.338,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.702,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.112,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.294,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.399,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.47,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.521,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.593,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.676,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[83,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7506","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[83,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 2","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"app - 1","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[65.439,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.229,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.849,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.236,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[63.226,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[61.296,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[59.111,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[58.033,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[57.388,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.945,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.616,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.359,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.154,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.984,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.842,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.72,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.616,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.525,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.447,15,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.378,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.317,15,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.265,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.219,15,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.178,15,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.143,15,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.113,15,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.066,15,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.012,15,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55,15,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.092,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.403,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.986,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[57.027,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[59.212,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[61.67,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[62.762,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[63.396,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[63.825,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.141,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.385,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.578,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.736,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.867,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.977,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.07,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.149,15,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.217,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.274,15,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.323,15,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.364,15,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.426,15,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.491,15,0],"t":407,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[55,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7505","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[55,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"divider","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":90,"ix":10},"p":{"k":[{"s":[51,15,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.913,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.615,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.073,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.194,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[47.751,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[45.001,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.869,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[40.328,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[39.409,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.778,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.309,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.941,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.645,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.402,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.199,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.025,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.876,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.747,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.635,15,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.536,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.451,15,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.376,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.31,15,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.253,15,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.203,15,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.161,15,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.124,15,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.093,15,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.068,15,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.047,15,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.017,15,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36,15,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.129,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.569,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.403,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.895,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.999,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[45.522,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[47.088,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[47.994,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[48.607,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.059,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.407,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.683,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.909,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.096,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.253,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.386,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.499,15,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.596,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.677,15,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.747,15,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.806,15,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.854,15,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.895,15,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.927,15,0],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.973,15,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"k":[{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2,-0.5],[2,-0.5]],"c":false}],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.019,-0.5],[2.019,-0.5]],"c":false}],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.077,-0.5],[2.077,-0.5]],"c":false}],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.185,-0.5],[2.185,-0.5]],"c":false}],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.361,-0.5],[2.361,-0.5]],"c":false}],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.65,-0.5],[2.65,-0.5]],"c":false}],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.2,-0.5],[3.2,-0.5]],"c":false}],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.829,-0.5],[3.829,-0.5]],"c":false}],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.136,-0.5],[4.136,-0.5]],"c":false}],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.318,-0.5],[4.318,-0.5]],"c":false}],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.444,-0.5],[4.444,-0.5]],"c":false}],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.538,-0.5],[4.538,-0.5]],"c":false}],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.612,-0.5],[4.612,-0.5]],"c":false}],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.671,-0.5],[4.671,-0.5]],"c":false}],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.72,-0.5],[4.72,-0.5]],"c":false}],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.761,-0.5],[4.761,-0.5]],"c":false}],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.796,-0.5],[4.796,-0.5]],"c":false}],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.826,-0.5],[4.826,-0.5]],"c":false}],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.852,-0.5],[4.852,-0.5]],"c":false}],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.874,-0.5],[4.874,-0.5]],"c":false}],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.894,-0.5],[4.894,-0.5]],"c":false}],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.91,-0.5],[4.91,-0.5]],"c":false}],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.925,-0.5],[4.925,-0.5]],"c":false}],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.938,-0.5],[4.938,-0.5]],"c":false}],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.949,-0.5],[4.949,-0.5]],"c":false}],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.959,-0.5],[4.959,-0.5]],"c":false}],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.967,-0.5],[4.967,-0.5]],"c":false}],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.974,-0.5],[4.974,-0.5]],"c":false}],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.98,-0.5],[4.98,-0.5]],"c":false}],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.99,-0.5],[4.99,-0.5]],"c":false}],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.996,-0.5],[4.996,-0.5]],"c":false}],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-5,-0.5],[5,-0.5]],"c":false}],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.973,-0.5],[4.973,-0.5]],"c":false}],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.887,-0.5],[4.887,-0.5]],"c":false}],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.72,-0.5],[4.72,-0.5]],"c":false}],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.421,-0.5],[4.421,-0.5]],"c":false}],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.8,-0.5],[3.8,-0.5]],"c":false}],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.093,-0.5],[3.093,-0.5]],"c":false}],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.782,-0.5],[2.782,-0.5]],"c":false}],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.601,-0.5],[2.601,-0.5]],"c":false}],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.479,-0.5],[2.479,-0.5]],"c":false}],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.388,-0.5],[2.388,-0.5]],"c":false}],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.318,-0.5],[2.318,-0.5]],"c":false}],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.263,-0.5],[2.263,-0.5]],"c":false}],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.217,-0.5],[2.217,-0.5]],"c":false}],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.18,-0.5],[2.18,-0.5]],"c":false}],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.148,-0.5],[2.148,-0.5]],"c":false}],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.122,-0.5],[2.122,-0.5]],"c":false}],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.099,-0.5],[2.099,-0.5]],"c":false}],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.081,-0.5],[2.081,-0.5]],"c":false}],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.064,-0.5],[2.064,-0.5]],"c":false}],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.051,-0.5],[2.051,-0.5]],"c":false}],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.039,-0.5],[2.039,-0.5]],"c":false}],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.03,-0.5],[2.03,-0.5]],"c":false}],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.022,-0.5],[2.022,-0.5]],"c":false}],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.01,-0.5],[2.01,-0.5]],"c":false}],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.006,-0.5],[2.006,-0.5]],"c":false}],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.004,-0.5],[2.004,-0.5]],"c":false}],"t":407,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.001,-0.5],[2.001,-0.5]],"c":false}],"t":408,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"divider","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[-52.349,0.652,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.453,0.652,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.813,0.652,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.464,0.652,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-54.515,0.652,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-56.247,0.652,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-59.565,0.652,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-63.323,0.652,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-65.162,0.652,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-66.258,0.652,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-67.015,0.652,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-67.578,0.652,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.019,0.652,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.375,0.652,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.668,0.652,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.914,0.652,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.122,0.652,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.301,0.652,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.456,0.652,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.59,0.652,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.708,0.652,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.81,0.652,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.9,0.652,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.978,0.652,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.047,0.652,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.106,0.652,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.157,0.652,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.2,0.652,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.268,0.652,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.328,0.652,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.349,0.652,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.193,0.652,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.662,0.652,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.662,0.652,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-66.874,0.652,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-63.132,0.652,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-58.906,0.652,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-57.04,0.652,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-55.956,0.652,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-55.22,0.652,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-54.678,0.652,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-54.259,0.652,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.925,0.652,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.654,0.652,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.43,0.652,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.241,0.652,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.082,0.652,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.947,0.652,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.831,0.652,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.734,0.652,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.65,0.652,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.58,0.652,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.522,0.652,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.474,0.652,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.435,0.652,0],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.404,0.652,0],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.354,0.652,0],"t":408,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[6.826,6.826,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 12","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,9.501],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 12","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 11","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 11","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 5","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[9.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.185,0.148],[0,0],[0,0],[0,0],[-0.086,0.24],[0,0.271],[0.468,0.462],[0.671,0],[0.468,-0.468],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.24,0.086]],"o":[[0,0],[0,0],[0,0],[0.148,-0.185],[0.086,-0.24],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.462,0.462],[0,0.671],[0.468,0.462],[0.271,0],[0.24,-0.086]],"v":[[0.48,0.998],[2.809,3.326],[3.326,2.809],[0.998,0.48],[1.349,-0.157],[1.478,-0.924],[0.776,-2.624],[-0.924,-3.326],[-2.633,-2.624],[-3.326,-0.924],[-2.633,0.785],[-0.924,1.478],[-0.157,1.349]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462],[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462]],"o":[[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462],[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462]],"v":[[0.249,0.259],[-0.924,0.739],[-2.106,0.259],[-2.587,-0.924],[-2.106,-2.097],[-0.924,-2.587],[0.249,-2.097],[0.739,-0.924]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 2","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0.4,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"icon","np":6,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[10.326,10.326],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"icon","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[91,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"k":[{"s":[120,4],"t":155,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.383,4.161],"t":156,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.592,4.668],"t":157,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[123.818,5.601],"t":158,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[127.463,7.13],"t":159,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[133.427,9.631],"t":160,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[144.8,14.4],"t":161,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[157.801,19.852],"t":162,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[164.141,22.511],"t":163,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[167.915,24.093],"t":164,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[170.516,25.184],"t":165,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[172.458,25.999],"t":166,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[173.978,26.636],"t":167,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[175.203,27.15],"t":168,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[176.216,27.574],"t":169,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[177.065,27.931],"t":170,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[177.788,28.234],"t":171,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[178.406,28.493],"t":172,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[178.938,28.716],"t":173,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[179.399,28.909],"t":174,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[179.8,29.078],"t":175,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.149,29.224],"t":176,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.454,29.352],"t":177,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.718,29.463],"t":178,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.949,29.559],"t":179,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.148,29.643],"t":180,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.32,29.715],"t":181,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.467,29.777],"t":182,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.592,29.829],"t":183,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.697,29.873],"t":184,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.784,29.91],"t":185,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.855,29.939],"t":186,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.909,29.962],"t":187,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.978,29.991],"t":189,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[182,30],"t":380,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.445,29.767],"t":381,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[179.655,29.017],"t":382,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[176.204,27.569],"t":383,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[170.034,24.982],"t":384,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[157.2,19.6],"t":385,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[142.586,13.472],"t":386,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[136.154,10.774],"t":387,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[132.424,9.21],"t":388,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[129.891,8.148],"t":389,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[128.022,7.364],"t":390,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[126.579,6.759],"t":391,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[125.427,6.276],"t":392,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[124.487,5.881],"t":393,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[123.712,5.556],"t":394,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[123.062,5.284],"t":395,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[122.517,5.055],"t":396,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[122.055,4.862],"t":397,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.663,4.697],"t":398,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.332,4.559],"t":399,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.051,4.441],"t":400,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.815,4.342],"t":401,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.62,4.26],"t":402,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.456,4.191],"t":403,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.323,4.135],"t":404,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.216,4.091],"t":405,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.133,4.056],"t":406,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.073,4.03],"t":407,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.03,4.013],"t":408,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.008,4.003],"t":409,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":32.672,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Taskbar Lofi","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"Recents_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[339.937,151.75,0],"ix":2,"l":2},"a":{"a":0,"k":[339.937,151.75,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.021,-1.766],[0,0],[-2.043,0],[0,0],[1.022,1.767]],"o":[[-1.021,-1.766],[0,0],[-1.022,1.767],[0,0],[2.043,0],[0,0]],"v":[[2.297,-7.675],[-2.297,-7.675],[-9.64,5.025],[-7.343,9],[7.343,9],[9.64,5.025]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":9,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[481.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[457.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[292,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[334,279],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[109,28],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[425.5,208.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[160,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[400,158.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[126,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[251,78.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[334,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[340,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":16,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82,171.125,0],"ix":2,"l":2},"a":{"a":0,"k":[82,171.125,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,177.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,165.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 3","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,171.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 2","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82.5,140.5,0],"ix":2,"l":2},"a":{"a":0,"k":[82,140.938,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Search","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,31.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"header","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,257.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 6","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,245.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,251.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 3","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,64],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,171],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"block","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,96.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,84.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,90.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 1","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app only","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":2,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":37,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":47,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[100]},{"t":256,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,29.984,0],"t":127,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.965,0],"t":128,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.936,0],"t":129,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.894,0],"t":130,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.84,0],"t":131,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.77,0],"t":132,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.682,0],"t":133,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.574,0],"t":134,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.445,0],"t":135,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.294,0],"t":136,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.121,0],"t":137,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.925,0],"t":138,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.746,0],"t":139,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.548,0],"t":140,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.33,0],"t":141,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.092,0],"t":142,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.832,0],"t":143,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.548,0],"t":144,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.239,0],"t":145,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.903,0],"t":146,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.536,0],"t":147,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.14,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,25.709,0],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,25.241,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,24.73,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,24.171,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,23.563,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,22.898,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,22.171,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,21.373,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,20.496,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,19.524,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,18.451,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,17.263,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,15.943,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,14.475,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,12.841,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,11.018,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,9.023,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,6.87,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,4.614,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,2.333,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0.106,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-1.975,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-3.877,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-5.591,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-7.125,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-8.492,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-9.714,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-10.799,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-11.771,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-12.643,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-13.428,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-14.138,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-14.777,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-15.355,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-15.879,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-16.354,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-16.784,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.177,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.532,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.854,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.146,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.409,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.645,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.858,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.048,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.217,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.366,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.496,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.61,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.707,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.788,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.856,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.911,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.954,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.984,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}],"ix":1}}]}],"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":248,"s":[33,0],"to":[0,0],"ti":[0,0]},{"t":258,"s":[41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"right circle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[-41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":248,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"t":258,"s":[-41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"left circle","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"size","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":37,"op":345,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Recents_EDU Loop","parent":5,"tt":1,"tp":5,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":0,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":511,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}],"markers":[{"tm":142,"cm":"drag with gesture","dr":108},{"tm":217,"cm":"onPause","dr":0},{"tm":250,"cm":"release playback realtime","dr":36}],"props":{}}
\ No newline at end of file
+{"v":"5.12.1","fr":60,"ip":0,"op":511,"w":554,"h":564,"nm":"Trackpad-JSON_Recents-EDU","ddd":0,"assets":[{"id":"comp_0","nm":"Recents_EDU Loop","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"CNTL || playback","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":0,"ix":3},"y":{"a":0,"k":0,"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Picker","np":3,"mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ","ix":1,"en":1,"ef":[{"ty":7,"nm":"Menu","mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ-0001","ix":1,"v":{"a":0,"k":2,"ix":1}}]},{"ty":5,"nm":"OUTPUT","np":3,"mn":"ADBE Slider Control","ix":2,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"k":[{"s":[0],"t":142,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.001],"t":143,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.002],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.003],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.004],"t":146,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.006],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.008],"t":148,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.01],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.012],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.016],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.02],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.025],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.031],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.038],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.047],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.059],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.073],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.091],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.116],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.15],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.196],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.249],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.306],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.366],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.425],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.481],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.53],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.575],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.614],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.648],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.678],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.706],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.73],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.752],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.772],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.79],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.807],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.822],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.836],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.849],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.861],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.873],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.883],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.892],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.901],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.91],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.917],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.925],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.931],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.937],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.943],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.949],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.954],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.958],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.963],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.967],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.97],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.974],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.977],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.98],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.983],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.985],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.987],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.989],"t":205,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.991],"t":206,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.993],"t":207,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.994],"t":208,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.996],"t":209,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.997],"t":210,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.998],"t":211,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.998],"t":212,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.999],"t":213,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1],"t":215,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.009],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.038],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.093],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.193],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.4],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.636],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.739],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.8],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.84],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.871],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.894],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.912],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.928],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.94],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.951],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.959],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.967],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.973],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.979],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.983],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.987],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.99],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.993],"t":273,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.995],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.997],"t":275,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.998],"t":276,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.999],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2],"t":380,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.009],"t":381,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.038],"t":382,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.093],"t":383,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.193],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.4],"t":385,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.636],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.739],"t":387,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.8],"t":388,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.84],"t":389,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.871],"t":390,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.894],"t":391,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.912],"t":392,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.928],"t":393,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.94],"t":394,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.951],"t":395,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.959],"t":396,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.967],"t":397,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.973],"t":398,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.979],"t":399,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.983],"t":400,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.987],"t":401,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.99],"t":402,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.993],"t":403,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.995],"t":404,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.997],"t":405,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.999],"t":408,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}}]},{"ty":5,"nm":"Keys","np":3,"mn":"ADBE Slider Control","ix":3,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.831],"y":[0.109]},"o":{"x":[0.458],"y":[0.053]},"t":142,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.15],"y":[0.43]},"t":161,"s":[0.15]},{"t":217,"s":[1],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[1]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[1.4]},{"t":280,"s":[2],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[2]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":385,"s":[2.4]},{"t":410,"s":[3]}],"ix":1}}]},{"ty":5,"nm":"State (holds)","np":3,"mn":"ADBE Slider Control","ix":4,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":0,"k":0,"ix":1}}]}],"shapes":[],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"Null :: Taskbar drop","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[252,278,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,278.45,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,279.615,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,281.252,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,283.166,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,287.233,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,289.181,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,290.982,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,292.599,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,294.012,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,295.216,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,296.216,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,297.023,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,297.655,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.131,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.474,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.705,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.465,0],"t":212,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.226,0],"t":215,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298,0],"t":377,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.382,0],"t":378,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,299.372,0],"t":379,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,300.764,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,302.391,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,305.848,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,307.504,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,309.035,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,310.409,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,311.611,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,312.634,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,313.483,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,314.169,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,314.706,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.112,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.403,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.717,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.474,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.192,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"Taskbar Lofi","parent":3,"refId":"comp_1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":134,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":143,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":432,"s":[100]},{"t":444,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":0,"ix":3},"y":{"k":[{"s":[26.984],"t":127,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.971],"t":128,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.95],"t":129,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.921],"t":130,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.882],"t":131,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.83],"t":132,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.765],"t":133,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.685],"t":134,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.589],"t":135,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.478],"t":136,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.349],"t":137,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.205],"t":138,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.072],"t":139,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.926],"t":140,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.764],"t":141,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.589],"t":142,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.397],"t":143,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.187],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.959],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.711],"t":146,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.44],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.146],"t":148,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.826],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.479],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.1],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.686],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.236],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[21.745],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[21.207],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[20.616],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.967],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.248],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.457],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.578],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.602],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.514],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.303],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[12.954],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[11.477],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[9.885],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[8.215],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[6.526],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[4.878],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[3.338],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.928],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.659],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.475],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-1.485],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-2.388],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-3.192],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-3.911],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-4.556],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-5.136],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-5.662],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-6.135],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-6.563],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-6.951],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-7.303],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-7.622],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-7.913],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.175],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.413],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.628],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.823],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.998],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.155],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.296],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.42],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.531],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.627],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.711],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.783],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.843],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.893],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.933],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.963],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.984],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.996],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[91,15,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}],"ix":1}}]}],"w":182,"h":30,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":3,"nm":"Focus Task :: Lift & Drop","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":252,"ix":3},"y":{"k":[{"s":[157.385],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.28],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.128],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.026],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.901],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.75],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.564],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.335],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.054],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.706],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.275],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[154.73],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[154.03],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[153.103],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[151.8],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[150.035],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[148.047],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.867],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[143.589],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[141.341],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[139.241],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[137.346],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[135.666],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[134.185],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[132.878],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[131.718],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[130.684],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[129.755],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[128.916],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[128.155],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[127.462],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[126.829],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[126.249],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[125.715],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[125.221],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.765],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.343],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[123.951],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[123.587],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[123.249],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.934],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.641],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.369],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.114],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.877],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.657],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.452],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.26],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.082],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.918],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.764],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.623],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.492],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.371],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.261],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.158],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.065],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.98],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.903],"t":205,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.835],"t":206,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.718],"t":208,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.629],"t":210,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.51],"t":215,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.5],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.746],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.54],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.071],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.808],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[130.5],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[136.982],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[139.835],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[141.489],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[142.613],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[143.442],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.082],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.593],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.01],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.354],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.642],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.884],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.089],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.262],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.409],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.534],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.638],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.725],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.857],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147],"t":380,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147.094],"t":381,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147.397],"t":382,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147.982],"t":383,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[149.027],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[151.2],"t":385,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[153.675],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[154.764],"t":387,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.396],"t":388,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.825],"t":389,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.141],"t":390,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.386],"t":391,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.581],"t":392,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.74],"t":393,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.871],"t":394,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.981],"t":395,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.074],"t":396,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.218],"t":398,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.362],"t":401,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"matte","parent":5,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"k":[{"s":[503.613,314.758],"t":144,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[503.134,314.459],"t":146,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.832,314.27],"t":147,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.464,314.04],"t":148,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.025,313.765],"t":149,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[501.487,313.429],"t":150,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[500.824,313.015],"t":151,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[500.023,312.514],"t":152,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[499.032,311.895],"t":153,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[497.818,311.136],"t":154,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[496.328,310.205],"t":155,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[494.484,309.053],"t":156,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[492.194,307.621],"t":157,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[489.307,305.817],"t":158,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[485.592,303.495],"t":159,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[480.67,300.419],"t":160,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[473.76,296.1],"t":161,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[464.395,290.247],"t":162,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[453.849,283.656],"t":163,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[442.286,276.429],"t":164,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[430.198,268.874],"t":165,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[418.274,261.421],"t":166,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[407.131,254.457],"t":167,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[397.077,248.173],"t":168,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[388.165,242.603],"t":169,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[380.31,237.694],"t":170,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[373.373,233.358],"t":171,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[367.218,229.511],"t":172,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[361.732,226.082],"t":173,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[356.803,223.002],"t":174,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[352.354,220.221],"t":175,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[348.318,217.699],"t":176,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[344.643,215.402],"t":177,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[341.283,213.302],"t":178,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[338.205,211.378],"t":179,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[335.37,209.606],"t":180,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[332.752,207.97],"t":181,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[330.33,206.456],"t":182,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[328.092,205.058],"t":183,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[326.012,203.757],"t":184,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[324.082,202.552],"t":185,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[322.291,201.432],"t":186,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[320.617,200.386],"t":187,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[319.062,199.414],"t":188,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[317.618,198.512],"t":189,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[316.267,197.667],"t":190,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[315.013,196.883],"t":191,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[313.845,196.153],"t":192,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[312.756,195.472],"t":193,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[311.738,194.837],"t":194,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[310.793,194.246],"t":195,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[309.921,193.7],"t":196,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[309.107,193.192],"t":197,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[308.359,192.724],"t":198,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[307.663,192.289],"t":199,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[307.02,191.888],"t":200,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[306.436,191.522],"t":201,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[305.891,191.182],"t":202,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[305.399,190.874],"t":203,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[304.946,190.591],"t":204,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[304.539,190.337],"t":205,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[304.178,190.112],"t":206,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.85,189.906],"t":207,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.555,189.722],"t":208,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.306,189.566],"t":209,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.082,189.427],"t":210,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.892,189.308],"t":211,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.617,189.135],"t":213,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.4,189],"t":250,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.247,188.904],"t":251,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[301.752,188.595],"t":252,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[300.798,187.999],"t":253,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[299.093,186.933],"t":254,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[295.546,184.716],"t":255,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[291.506,182.192],"t":256,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[289.729,181.08],"t":257,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[288.698,180.436],"t":258,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.998,179.999],"t":259,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.481,179.676],"t":260,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.082,179.427],"t":261,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.764,179.227],"t":262,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.504,179.065],"t":263,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.29,178.931],"t":264,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.11,178.819],"t":265,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.832,178.645],"t":267,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.555,178.472],"t":270,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.272,178.295],"t":278,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.264,178.29],"t":380,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.222,179.514],"t":381,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[293.538,183.461],"t":382,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[305.714,191.071],"t":383,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[327.48,204.675],"t":384,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[372.758,232.974],"t":385,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[424.317,265.198],"t":386,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[447.009,279.381],"t":387,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[460.167,287.605],"t":388,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[469.103,293.19],"t":389,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[475.697,297.31],"t":390,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[480.788,300.492],"t":391,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[484.853,303.033],"t":392,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[488.172,305.107],"t":393,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[490.906,306.816],"t":394,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[493.198,308.249],"t":395,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[495.121,309.451],"t":396,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[496.752,310.47],"t":397,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[498.133,311.333],"t":398,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[499.301,312.063],"t":399,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[500.29,312.681],"t":400,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[501.123,313.202],"t":401,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[501.814,313.634],"t":402,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.391,313.994],"t":403,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.861,314.288],"t":404,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[503.238,314.524],"t":405,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[503.53,314.706],"t":406,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0],"ix":3},"r":{"k":[{"s":[27.974],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.959],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.942],"t":146,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.922],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.898],"t":148,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.869],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.833],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.789],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.736],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.67],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.589],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.49],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.368],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.216],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.024],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.777],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.45],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.991],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.37],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.669],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.901],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.098],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.306],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[21.566],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[20.898],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[20.306],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.785],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.324],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.915],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.551],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.223],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.928],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.66],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.416],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.193],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.988],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.8],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.626],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.465],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.316],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.178],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.05],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.931],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.82],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.717],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.621],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.531],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.448],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.37],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.298],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.23],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.167],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.11],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.055],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.006],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.96],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.917],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.878],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.842],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.809],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.779],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.752],"t":205,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.728],"t":206,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.706],"t":207,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.687],"t":208,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.67],"t":209,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.655],"t":210,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.643],"t":211,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.633],"t":212,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.624],"t":213,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.61],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.603],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.579],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.532],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.45],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.278],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.082],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.996],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.946],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.912],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.887],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.868],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.853],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.84],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.83],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.821],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.808],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.794],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.78],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.78],"t":380,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.907],"t":381,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.318],"t":382,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.109],"t":383,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.524],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.468],"t":385,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.82],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.295],"t":387,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.15],"t":388,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.731],"t":389,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.16],"t":390,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.491],"t":391,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.755],"t":392,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.971],"t":393,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.149],"t":394,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.298],"t":395,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.423],"t":396,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.529],"t":397,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.619],"t":398,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.694],"t":399,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.759],"t":400,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.813],"t":401,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.858],"t":402,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.895],"t":403,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.926],"t":404,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.95],"t":405,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.969],"t":406,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.993],"t":408,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Recents_LofiApp","parent":6,"tt":1,"tp":6,"refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"k":[{"s":[99.923,99.923,100],"t":144,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.828,99.828,100],"t":146,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.768,99.768,100],"t":147,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.695,99.695,100],"t":148,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.608,99.608,100],"t":149,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.501,99.501,100],"t":150,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.37,99.37,100],"t":151,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.211,99.211,100],"t":152,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.014,99.014,100],"t":153,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.773,98.773,100],"t":154,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.478,98.478,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.112,98.112,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.658,97.658,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.085,97.085,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.348,96.348,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.371,95.371,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94,94,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.142,92.142,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.049,90.049,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[87.755,87.755,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.357,85.357,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.991,82.991,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.78,80.78,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[78.785,78.785,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[77.017,77.017,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[75.458,75.458,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[74.082,74.082,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[72.861,72.861,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[71.772,71.772,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70.794,70.794,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[69.911,69.911,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[69.111,69.111,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.382,68.382,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[67.715,67.715,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[67.104,67.104,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[66.542,66.542,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[66.022,66.022,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[65.542,65.542,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[65.098,65.098,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[64.685,64.685,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[64.302,64.302,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.947,63.947,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.615,63.615,100],"t":187,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.306,63.306,100],"t":188,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.02,63.02,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.751,62.751,100],"t":190,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.503,62.503,100],"t":191,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.271,62.271,100],"t":192,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.055,62.055,100],"t":193,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.853,61.853,100],"t":194,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.665,61.665,100],"t":195,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.492,61.492,100],"t":196,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.331,61.331,100],"t":197,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.182,61.182,100],"t":198,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.044,61.044,100],"t":199,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.917,60.917,100],"t":200,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.801,60.801,100],"t":201,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.693,60.693,100],"t":202,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.595,60.595,100],"t":203,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.505,60.505,100],"t":204,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.424,60.424,100],"t":205,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.353,60.353,100],"t":206,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.288,60.288,100],"t":207,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.229,60.229,100],"t":208,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.18,60.18,100],"t":209,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.135,60.135,100],"t":210,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.098,60.098,100],"t":211,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.043,60.043,100],"t":213,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60,60,100],"t":250,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.97,59.97,100],"t":251,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.871,59.871,100],"t":252,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.682,59.682,100],"t":253,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.344,59.344,100],"t":254,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[58.64,58.64,100],"t":255,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.839,57.839,100],"t":256,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.486,57.486,100],"t":257,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.281,57.281,100],"t":258,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.142,57.142,100],"t":259,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.04,57.04,100],"t":260,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.961,56.961,100],"t":261,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.898,56.898,100],"t":262,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.846,56.846,100],"t":263,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.804,56.804,100],"t":264,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.768,56.768,100],"t":265,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.713,56.713,100],"t":267,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.658,56.658,100],"t":270,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.602,56.602,100],"t":278,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.6,56.6,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.989,56.989,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[58.242,58.242,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.657,60.657,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[64.976,64.976,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[73.96,73.96,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.19,84.19,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.692,88.692,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[91.303,91.303,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.076,93.076,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.384,94.384,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.394,95.394,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.201,96.201,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.859,96.859,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.402,97.402,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.857,97.857,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.238,98.238,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.562,98.562,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.836,98.836,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.068,99.068,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.264,99.264,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.429,99.429,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.566,99.566,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.681,99.681,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.774,99.774,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.849,99.849,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.907,99.907,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":7,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"t":277,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,-30.035,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[176.678,176.678,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"second Tasks Zoom back","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":385,"s":[98,98,100]},{"t":410,"s":[95,95,100]}],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Null :: Reposition Side Task","parent":9,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-318.4,-38,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-277.34,-48.1,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,-63.25,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":12,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-111.72,0],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-111.197,0],"t":251,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-109.514,0],"t":252,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-106.268,0],"t":253,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-100.462,0],"t":254,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-88.39,0],"t":255,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-74.643,0],"t":256,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-68.591,0],"t":257,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-65.083,0],"t":258,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-62.7,0],"t":259,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-60.943,0],"t":260,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-59.584,0],"t":261,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-58.5,0],"t":262,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-57.616,0],"t":263,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.886,0],"t":264,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.276,0],"t":265,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.762,0],"t":266,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.328,0],"t":267,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.96,0],"t":268,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.648,0],"t":269,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.385,0],"t":270,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.163,0],"t":271,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.977,0],"t":272,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.824,0],"t":273,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.698,0],"t":274,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.598,0],"t":275,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.521,0],"t":276,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.463,0],"t":277,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.424,0],"t":278,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":10,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.963},"t":217,"s":[-84.8,0,0],"to":[0,0,0],"ti":[0,0,0]},{"t":250,"s":[0,0,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":250,"s":[302.4,189]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":255,"s":[227.56,142.34]},{"t":280,"s":[115.3,72.35]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[14.6]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[14.272]},{"t":280,"s":[13.78]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":14,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-53.175,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.175,0],"t":510,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-411.95,20.325,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-333.47,29.183,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,42.47,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[115.3,72.35],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13.78,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Taskbar Lofi","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[51.5,0,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.652,0,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.136,0,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[53.013,0,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[54.449,0,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.806,0,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[61.3,0,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[66.437,0,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[68.94,0,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[70.432,0,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[71.462,0,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[72.229,0,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[72.83,0,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[73.314,0,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[73.714,0,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.048,0,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.334,0,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.578,0,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.789,0,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.971,0,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.131,0,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.269,0,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.389,0,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.493,0,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.584,0,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.663,0,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.731,0,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.789,0,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.839,0,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.915,0,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.982,0,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[76,0,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.779,0,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.066,0,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[73.709,0,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[71.271,0,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[66.2,0,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[60.425,0,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[57.886,0,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.41,0,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.409,0,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[54.67,0,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[54.1,0,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[53.646,0,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[53.275,0,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.968,0,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.711,0,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.495,0,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.312,0,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.157,0,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.026,0,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.916,0,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.822,0,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.745,0,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.68,0,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.628,0,0],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.585,0,0],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.552,0,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.501,0,0],"t":409,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[167,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7511","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[167,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 5","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[123.341,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.654,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.223,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[125.146,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[126.662,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[129.549,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[132.838,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[134.455,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[135.421,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[136.083,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[136.576,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[136.962,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.272,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.528,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.742,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.924,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.08,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.216,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.334,15,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.437,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.527,15,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.606,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.734,15,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.869,15,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.864,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.402,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.527,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[135.96,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[132.701,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[129.002,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[127.358,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[126.406,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[125.763,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[125.288,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.923,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.633,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.396,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.199,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.034,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.895,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.776,15,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.675,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.589,15,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.516,15,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.403,15,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.299,15,0],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[139,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7508","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[139,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[104.041,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.182,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.436,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.844,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.517,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[106.808,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[108.265,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[108.981,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.409,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.703,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.923,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.092,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.228,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.341,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.436,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.517,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.587,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.648,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.746,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.853,15,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.956,15,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.938,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.73,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.34,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.649,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[108.197,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[106.559,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.828,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.403,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.117,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.906,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.745,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.616,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.511,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.424,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.35,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.288,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.19,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.091,15,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[111,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7507","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[111,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 3","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[84.704,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.639,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.537,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.371,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.048,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.684,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.505,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.398,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.324,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.271,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.195,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.123,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.045,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.068,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.166,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.338,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.702,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.112,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.294,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.399,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.47,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.521,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.593,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.676,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[83,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7506","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[83,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 2","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[65.439,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.229,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.849,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.236,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[63.226,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[61.296,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[59.111,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[58.033,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[57.388,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.945,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.616,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.359,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.154,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.984,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.842,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.72,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.616,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.525,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.447,15,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.378,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.317,15,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.265,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.219,15,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.178,15,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.143,15,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.113,15,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.066,15,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.012,15,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55,15,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.092,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.403,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.986,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[57.027,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[59.212,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[61.67,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[62.762,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[63.396,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[63.825,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.141,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.385,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.578,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.736,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.867,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.977,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.07,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.149,15,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.217,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.274,15,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.323,15,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.364,15,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.426,15,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.491,15,0],"t":407,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[55,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7505","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[55,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":90,"ix":10},"p":{"k":[{"s":[51,15,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.913,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.615,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.073,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.194,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[47.751,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[45.001,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.869,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[40.328,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[39.409,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.778,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.309,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.941,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.645,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.402,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.199,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.025,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.876,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.747,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.635,15,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.536,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.451,15,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.376,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.31,15,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.253,15,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.203,15,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.161,15,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.124,15,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.093,15,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.068,15,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.047,15,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.017,15,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36,15,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.129,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.569,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.403,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.895,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.999,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[45.522,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[47.088,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[47.994,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[48.607,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.059,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.407,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.683,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.909,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.096,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.253,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.386,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.499,15,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.596,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.677,15,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.747,15,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.806,15,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.854,15,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.895,15,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.927,15,0],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.973,15,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"k":[{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2,-0.5],[2,-0.5]],"c":false}],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.019,-0.5],[2.019,-0.5]],"c":false}],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.077,-0.5],[2.077,-0.5]],"c":false}],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.185,-0.5],[2.185,-0.5]],"c":false}],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.361,-0.5],[2.361,-0.5]],"c":false}],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.65,-0.5],[2.65,-0.5]],"c":false}],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.2,-0.5],[3.2,-0.5]],"c":false}],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.829,-0.5],[3.829,-0.5]],"c":false}],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.136,-0.5],[4.136,-0.5]],"c":false}],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.318,-0.5],[4.318,-0.5]],"c":false}],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.444,-0.5],[4.444,-0.5]],"c":false}],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.538,-0.5],[4.538,-0.5]],"c":false}],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.612,-0.5],[4.612,-0.5]],"c":false}],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.671,-0.5],[4.671,-0.5]],"c":false}],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.72,-0.5],[4.72,-0.5]],"c":false}],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.761,-0.5],[4.761,-0.5]],"c":false}],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.796,-0.5],[4.796,-0.5]],"c":false}],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.826,-0.5],[4.826,-0.5]],"c":false}],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.852,-0.5],[4.852,-0.5]],"c":false}],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.874,-0.5],[4.874,-0.5]],"c":false}],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.894,-0.5],[4.894,-0.5]],"c":false}],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.91,-0.5],[4.91,-0.5]],"c":false}],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.925,-0.5],[4.925,-0.5]],"c":false}],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.938,-0.5],[4.938,-0.5]],"c":false}],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.949,-0.5],[4.949,-0.5]],"c":false}],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.959,-0.5],[4.959,-0.5]],"c":false}],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.967,-0.5],[4.967,-0.5]],"c":false}],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.974,-0.5],[4.974,-0.5]],"c":false}],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.98,-0.5],[4.98,-0.5]],"c":false}],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.99,-0.5],[4.99,-0.5]],"c":false}],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.996,-0.5],[4.996,-0.5]],"c":false}],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-5,-0.5],[5,-0.5]],"c":false}],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.973,-0.5],[4.973,-0.5]],"c":false}],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.887,-0.5],[4.887,-0.5]],"c":false}],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.72,-0.5],[4.72,-0.5]],"c":false}],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.421,-0.5],[4.421,-0.5]],"c":false}],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.8,-0.5],[3.8,-0.5]],"c":false}],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.093,-0.5],[3.093,-0.5]],"c":false}],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.782,-0.5],[2.782,-0.5]],"c":false}],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.601,-0.5],[2.601,-0.5]],"c":false}],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.479,-0.5],[2.479,-0.5]],"c":false}],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.388,-0.5],[2.388,-0.5]],"c":false}],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.318,-0.5],[2.318,-0.5]],"c":false}],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.263,-0.5],[2.263,-0.5]],"c":false}],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.217,-0.5],[2.217,-0.5]],"c":false}],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.18,-0.5],[2.18,-0.5]],"c":false}],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.148,-0.5],[2.148,-0.5]],"c":false}],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.122,-0.5],[2.122,-0.5]],"c":false}],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.099,-0.5],[2.099,-0.5]],"c":false}],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.081,-0.5],[2.081,-0.5]],"c":false}],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.064,-0.5],[2.064,-0.5]],"c":false}],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.051,-0.5],[2.051,-0.5]],"c":false}],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.039,-0.5],[2.039,-0.5]],"c":false}],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.03,-0.5],[2.03,-0.5]],"c":false}],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.022,-0.5],[2.022,-0.5]],"c":false}],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.01,-0.5],[2.01,-0.5]],"c":false}],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.006,-0.5],[2.006,-0.5]],"c":false}],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.004,-0.5],[2.004,-0.5]],"c":false}],"t":407,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.001,-0.5],[2.001,-0.5]],"c":false}],"t":408,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"divider","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[-52.349,0.652,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.453,0.652,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.813,0.652,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.464,0.652,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-54.515,0.652,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-56.247,0.652,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-59.565,0.652,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-63.323,0.652,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-65.162,0.652,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-66.258,0.652,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-67.015,0.652,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-67.578,0.652,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.019,0.652,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.375,0.652,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.668,0.652,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.914,0.652,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.122,0.652,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.301,0.652,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.456,0.652,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.59,0.652,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.708,0.652,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.81,0.652,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.9,0.652,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.978,0.652,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.047,0.652,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.106,0.652,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.157,0.652,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.2,0.652,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.268,0.652,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.328,0.652,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.349,0.652,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.193,0.652,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.662,0.652,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.662,0.652,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-66.874,0.652,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-63.132,0.652,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-58.906,0.652,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-57.04,0.652,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-55.956,0.652,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-55.22,0.652,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-54.678,0.652,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-54.259,0.652,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.925,0.652,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.654,0.652,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.43,0.652,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.241,0.652,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.082,0.652,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.947,0.652,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.831,0.652,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.734,0.652,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.65,0.652,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.58,0.652,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.522,0.652,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.474,0.652,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.435,0.652,0],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.404,0.652,0],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.354,0.652,0],"t":408,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[6.826,6.826,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 12","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,9.501],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 12","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 11","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 11","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 5","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[9.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.185,0.148],[0,0],[0,0],[0,0],[-0.086,0.24],[0,0.271],[0.468,0.462],[0.671,0],[0.468,-0.468],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.24,0.086]],"o":[[0,0],[0,0],[0,0],[0.148,-0.185],[0.086,-0.24],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.462,0.462],[0,0.671],[0.468,0.462],[0.271,0],[0.24,-0.086]],"v":[[0.48,0.998],[2.809,3.326],[3.326,2.809],[0.998,0.48],[1.349,-0.157],[1.478,-0.924],[0.776,-2.624],[-0.924,-3.326],[-2.633,-2.624],[-3.326,-0.924],[-2.633,0.785],[-0.924,1.478],[-0.157,1.349]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462],[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462]],"o":[[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462],[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462]],"v":[[0.249,0.259],[-0.924,0.739],[-2.106,0.259],[-2.587,-0.924],[-2.106,-2.097],[-0.924,-2.587],[0.249,-2.097],[0.739,-0.924]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 2","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0.4,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"icon","np":6,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[10.326,10.326],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"icon","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[91,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"k":[{"s":[120,4],"t":155,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.383,4.161],"t":156,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.592,4.668],"t":157,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[123.818,5.601],"t":158,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[127.463,7.13],"t":159,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[133.427,9.631],"t":160,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[144.8,14.4],"t":161,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[157.801,19.852],"t":162,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[164.141,22.511],"t":163,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[167.915,24.093],"t":164,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[170.516,25.184],"t":165,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[172.458,25.999],"t":166,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[173.978,26.636],"t":167,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[175.203,27.15],"t":168,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[176.216,27.574],"t":169,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[177.065,27.931],"t":170,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[177.788,28.234],"t":171,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[178.406,28.493],"t":172,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[178.938,28.716],"t":173,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[179.399,28.909],"t":174,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[179.8,29.078],"t":175,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.149,29.224],"t":176,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.454,29.352],"t":177,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.718,29.463],"t":178,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.949,29.559],"t":179,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.148,29.643],"t":180,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.32,29.715],"t":181,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.467,29.777],"t":182,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.592,29.829],"t":183,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.697,29.873],"t":184,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.784,29.91],"t":185,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.855,29.939],"t":186,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.909,29.962],"t":187,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.978,29.991],"t":189,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[182,30],"t":380,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.445,29.767],"t":381,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[179.655,29.017],"t":382,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[176.204,27.569],"t":383,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[170.034,24.982],"t":384,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[157.2,19.6],"t":385,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[142.586,13.472],"t":386,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[136.154,10.774],"t":387,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[132.424,9.21],"t":388,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[129.891,8.148],"t":389,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[128.022,7.364],"t":390,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[126.579,6.759],"t":391,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[125.427,6.276],"t":392,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[124.487,5.881],"t":393,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[123.712,5.556],"t":394,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[123.062,5.284],"t":395,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[122.517,5.055],"t":396,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[122.055,4.862],"t":397,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.663,4.697],"t":398,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.332,4.559],"t":399,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.051,4.441],"t":400,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.815,4.342],"t":401,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.62,4.26],"t":402,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.456,4.191],"t":403,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.323,4.135],"t":404,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.216,4.091],"t":405,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.133,4.056],"t":406,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.073,4.03],"t":407,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.03,4.013],"t":408,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.008,4.003],"t":409,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":32.672,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Taskbar Lofi","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"Recents_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[339.937,151.75,0],"ix":2,"l":2},"a":{"a":0,"k":[339.937,151.75,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.021,-1.766],[0,0],[-2.043,0],[0,0],[1.022,1.767]],"o":[[-1.021,-1.766],[0,0],[-1.022,1.767],[0,0],[2.043,0],[0,0]],"v":[[2.297,-7.675],[-2.297,-7.675],[-9.64,5.025],[-7.343,9],[7.343,9],[9.64,5.025]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":9,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[481.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[457.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[292,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[334,279],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[109,28],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[425.5,208.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[160,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[400,158.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[126,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[251,78.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[334,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[340,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":16,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82,171.125,0],"ix":2,"l":2},"a":{"a":0,"k":[82,171.125,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,177.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,165.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 3","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,171.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 2","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82.5,140.5,0],"ix":2,"l":2},"a":{"a":0,"k":[82,140.938,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Search","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,31.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"header","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,257.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 6","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,245.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,251.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 3","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,64],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,171],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"block","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,96.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,84.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,90.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 1","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app only","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":2,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":37,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":47,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[100]},{"t":256,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,29.984,0],"t":127,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.965,0],"t":128,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.936,0],"t":129,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.894,0],"t":130,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.84,0],"t":131,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.77,0],"t":132,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.682,0],"t":133,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.574,0],"t":134,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.445,0],"t":135,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.294,0],"t":136,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.121,0],"t":137,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.925,0],"t":138,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.746,0],"t":139,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.548,0],"t":140,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.33,0],"t":141,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.092,0],"t":142,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.832,0],"t":143,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.548,0],"t":144,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.239,0],"t":145,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.903,0],"t":146,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.536,0],"t":147,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.14,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,25.709,0],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,25.241,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,24.73,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,24.171,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,23.563,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,22.898,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,22.171,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,21.373,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,20.496,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,19.524,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,18.451,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,17.263,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,15.943,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,14.475,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,12.841,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,11.018,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,9.023,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,6.87,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,4.614,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,2.333,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0.106,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-1.975,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-3.877,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-5.591,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-7.125,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-8.492,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-9.714,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-10.799,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-11.771,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-12.643,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-13.428,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-14.138,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-14.777,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-15.355,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-15.879,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-16.354,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-16.784,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.177,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.532,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.854,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.146,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.409,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.645,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.858,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.048,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.217,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.366,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.496,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.61,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.707,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.788,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.856,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.911,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.954,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.984,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}],"ix":1}}]}],"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":248,"s":[33,0],"to":[0,0],"ti":[0,0]},{"t":258,"s":[41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"right circle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[-41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":248,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"t":258,"s":[-41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"left circle","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"size","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":37,"op":345,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Recents_EDU Loop","parent":5,"tt":1,"tp":5,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":0,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":511,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}],"markers":[{"tm":142,"cm":"drag with gesture","dr":108},{"tm":217,"cm":"onPause","dr":0},{"tm":250,"cm":"release playback realtime","dr":36}],"props":{}}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/trackpad_recent_apps_success.json b/packages/SystemUI/res/raw/trackpad_recent_apps_success.json
index 1703c41..21a9e13 100644
--- a/packages/SystemUI/res/raw/trackpad_recent_apps_success.json
+++ b/packages/SystemUI/res/raw/trackpad_recent_apps_success.json
@@ -1 +1 @@
-{"v":"5.12.1","fr":60,"ip":0,"op":97,"w":554,"h":564,"nm":"Trackpad-JSON_Recents-Success","ddd":0,"assets":[{"id":"comp_0","nm":"TrackpadAK_Success_Checkmark","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Check Rotate","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":2,"s":[-16]},{"t":20,"s":[6]}],"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[95.049,95.049,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":228,"st":-72,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Bounce","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":12,"s":[0]},{"t":36,"s":[-6]}],"ix":10},"p":{"a":0,"k":[81,127,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.263,0.263,0.833],"y":[1.126,1.126,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.958,0.958,0]},"t":1,"s":[80,80,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.45,0.45,0.167],"y":[0.325,0.325,0]},"t":20,"s":[105,105,100]},{"t":36,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-0.289,"ix":10},"p":{"a":0,"k":[14.364,-33.591,0],"ix":2,"l":2},"a":{"a":0,"k":[-0.125,0,0],"ix":1,"l":2},"s":{"a":0,"k":[104.744,104.744,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.401,-0.007],[-10.033,11.235]],"o":[[5.954,7.288],[1.401,0.007],[0,0]],"v":[[-28.591,4.149],[-10.73,26.013],[31.482,-21.255]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":3,"s":[0]},{"i":{"x":[0.22],"y":[1]},"o":{"x":[0.001],"y":[0.149]},"t":10,"s":[29]},{"t":27,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":11,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":5,"op":44,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[95,95,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.275,0.275,0.21],"y":[1.102,1.102,1]},"o":{"x":[0.037,0.037,0.05],"y":[0.476,0.476,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.252,0.252,0.47],"y":[0.159,0.159,0]},"t":16,"s":[120,120,100]},{"t":28,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.32,0.32],"y":[0.11,0.11]},"t":16,"s":[148,148]},{"t":28,"s":[136,136]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":88,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Checkbox - Widget","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Trackpad-JSON_Recents-EDU","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":37,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":47,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[100]},{"t":256,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-20,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-20,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}],"ix":1}}]}],"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":248,"s":[33,0],"to":[0,0],"ti":[0,0]},{"t":258,"s":[41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"right circle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[-41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":248,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"t":258,"s":[-41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"left circle","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"size","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":37,"op":345,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Recents_EDU Loop","parent":5,"tt":1,"tp":5,"refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"Recents_EDU Loop","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"CNTL || playback","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":0,"ix":3},"y":{"a":0,"k":0,"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Picker","np":3,"mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ","ix":1,"en":1,"ef":[{"ty":7,"nm":"Menu","mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ-0001","ix":1,"v":{"a":0,"k":2,"ix":1}}]},{"ty":5,"nm":"OUTPUT","np":3,"mn":"ADBE Slider Control","ix":2,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"k":[{"s":[1],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.009],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.038],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.093],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.193],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.4],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.636],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.739],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.8],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.84],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.871],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.894],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.912],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.928],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.94],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.951],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.959],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.967],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.973],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.979],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.983],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.987],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.99],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.993],"t":273,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.995],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.997],"t":275,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.998],"t":276,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.999],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}}]},{"ty":5,"nm":"Keys","np":3,"mn":"ADBE Slider Control","ix":3,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.831],"y":[0.109]},"o":{"x":[0.458],"y":[0.053]},"t":142,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.15],"y":[0.43]},"t":161,"s":[0.15]},{"t":217,"s":[1],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[1]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[1.4]},{"t":280,"s":[2],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[2]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":385,"s":[2.4]},{"t":410,"s":[3]}],"ix":1}}]},{"ty":5,"nm":"State (holds)","np":3,"mn":"ADBE Slider Control","ix":4,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":0,"k":0,"ix":1}}]}],"shapes":[],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"Null :: Taskbar drop","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[252,298.112,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"Taskbar Lofi","parent":3,"refId":"comp_3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":134,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":143,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":432,"s":[100]},{"t":444,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":0,"ix":3},"y":{"k":[{"s":[-10],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-10],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[91,15,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}],"ix":1}}]}],"w":182,"h":30,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":3,"nm":"Focus Task :: Lift & Drop","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":252,"ix":3},"y":{"k":[{"s":[119.5],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.746],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.54],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.071],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.808],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[130.5],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[136.982],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[139.835],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[141.489],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[142.613],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[143.442],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.082],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.593],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.01],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.354],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.642],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.884],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.089],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.262],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.409],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.534],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.638],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.725],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.857],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"matte","parent":5,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"k":[{"s":[302.247,188.904],"t":251,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[301.752,188.595],"t":252,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[300.798,187.999],"t":253,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[299.093,186.933],"t":254,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[295.546,184.716],"t":255,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[291.506,182.192],"t":256,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[289.729,181.08],"t":257,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[288.698,180.436],"t":258,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.998,179.999],"t":259,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.481,179.676],"t":260,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.082,179.427],"t":261,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.764,179.227],"t":262,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.504,179.065],"t":263,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.29,178.931],"t":264,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.11,178.819],"t":265,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.832,178.645],"t":267,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.555,178.472],"t":270,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.272,178.295],"t":278,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0],"ix":3},"r":{"k":[{"s":[14.603],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.579],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.532],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.45],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.278],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.082],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.996],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.946],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.912],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.887],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.868],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.853],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.84],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.83],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.821],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.808],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.794],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.78],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Recents_LofiApp","parent":6,"tt":1,"tp":6,"refId":"comp_4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"k":[{"s":[59.97,59.97,100],"t":251,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.871,59.871,100],"t":252,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.682,59.682,100],"t":253,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.344,59.344,100],"t":254,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[58.64,58.64,100],"t":255,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.839,57.839,100],"t":256,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.486,57.486,100],"t":257,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.281,57.281,100],"t":258,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.142,57.142,100],"t":259,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.04,57.04,100],"t":260,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.961,56.961,100],"t":261,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.898,56.898,100],"t":262,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.846,56.846,100],"t":263,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.804,56.804,100],"t":264,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.768,56.768,100],"t":265,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.713,56.713,100],"t":267,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.658,56.658,100],"t":270,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.602,56.602,100],"t":278,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":7,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"t":277,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,-30.035,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[176.678,176.678,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"second Tasks Zoom back","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":385,"s":[98,98,100]},{"t":410,"s":[95,95,100]}],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Null :: Reposition Side Task","parent":9,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-318.4,-38,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-277.34,-48.1,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,-63.25,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":12,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-111.72,0],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-111.197,0],"t":251,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-109.514,0],"t":252,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-106.268,0],"t":253,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-100.462,0],"t":254,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-88.39,0],"t":255,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-74.643,0],"t":256,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-68.591,0],"t":257,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-65.083,0],"t":258,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-62.7,0],"t":259,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-60.943,0],"t":260,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-59.584,0],"t":261,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-58.5,0],"t":262,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-57.616,0],"t":263,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.886,0],"t":264,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.276,0],"t":265,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.762,0],"t":266,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.328,0],"t":267,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.96,0],"t":268,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.648,0],"t":269,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.385,0],"t":270,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.163,0],"t":271,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.977,0],"t":272,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.824,0],"t":273,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.698,0],"t":274,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.598,0],"t":275,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.521,0],"t":276,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.463,0],"t":277,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.424,0],"t":278,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":10,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.963},"t":217,"s":[-84.8,0,0],"to":[0,0,0],"ti":[0,0,0]},{"t":250,"s":[0,0,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":250,"s":[302.4,189]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":255,"s":[227.56,142.34]},{"t":280,"s":[115.3,72.35]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[14.6]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[14.272]},{"t":280,"s":[13.78]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":14,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-53.175,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.175,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-411.95,20.325,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-333.47,29.183,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,42.47,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[115.3,72.35],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13.78,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0}]},{"id":"comp_3","nm":"Taskbar Lofi","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"app - 5","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[76,0,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[76,0,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[167,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7511","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[167,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 5","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"app - 4","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[139,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[139,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[139,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7508","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[139,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"app - 3","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[111,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[111,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[111,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7507","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[111,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 3","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"app - 2","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[83,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[83,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7506","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[83,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 2","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"app - 1","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[55,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[55,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7505","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[55,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"divider","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":90,"ix":10},"p":{"k":[{"s":[36,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"k":[{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-5,-0.5],[5,-0.5]],"c":false}],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-5,-0.5],[5,-0.5]],"c":false}],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"divider","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[-70.349,0.652,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.349,0.652,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[6.826,6.826,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 12","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,9.501],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 12","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 11","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 11","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 5","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[9.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.185,0.148],[0,0],[0,0],[0,0],[-0.086,0.24],[0,0.271],[0.468,0.462],[0.671,0],[0.468,-0.468],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.24,0.086]],"o":[[0,0],[0,0],[0,0],[0.148,-0.185],[0.086,-0.24],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.462,0.462],[0,0.671],[0.468,0.462],[0.271,0],[0.24,-0.086]],"v":[[0.48,0.998],[2.809,3.326],[3.326,2.809],[0.998,0.48],[1.349,-0.157],[1.478,-0.924],[0.776,-2.624],[-0.924,-3.326],[-2.633,-2.624],[-3.326,-0.924],[-2.633,0.785],[-0.924,1.478],[-0.157,1.349]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462],[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462]],"o":[[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462],[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462]],"v":[[0.249,0.259],[-0.924,0.739],[-2.106,0.259],[-2.587,-0.924],[-2.106,-2.097],[-0.924,-2.587],[0.249,-2.097],[0.739,-0.924]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 2","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0.4,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"icon","np":6,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[10.326,10.326],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"icon","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[91,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"k":[{"s":[182,30],"t":217,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[182,30],"t":292,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":32.672,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Taskbar Lofi","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_4","nm":"Recents_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[339.937,151.75,0],"ix":2,"l":2},"a":{"a":0,"k":[339.937,151.75,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.021,-1.766],[0,0],[-2.043,0],[0,0],[1.022,1.767]],"o":[[-1.021,-1.766],[0,0],[-1.022,1.767],[0,0],[2.043,0],[0,0]],"v":[[2.297,-7.675],[-2.297,-7.675],[-9.64,5.025],[-7.343,9],[7.343,9],[9.64,5.025]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":9,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[481.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[457.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[292,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[334,279],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[109,28],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[425.5,208.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[160,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[400,158.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[126,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[251,78.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[334,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[340,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":16,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82,171.125,0],"ix":2,"l":2},"a":{"a":0,"k":[82,171.125,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,177.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,165.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 3","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,171.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 2","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82.5,140.5,0],"ix":2,"l":2},"a":{"a":0,"k":[82,140.938,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Search","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,31.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"header","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,257.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 6","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,245.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,251.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 3","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,64],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,171],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"block","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,96.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,84.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,90.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 1","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app only","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"TrackpadAK_Success_Checkmark","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,198.5,0],"ix":2,"l":2},"a":{"a":0,"k":[95,95,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":190,"h":190,"ip":53,"op":97,"st":53,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"track matte 3","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","tt":1,"tp":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":48,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":54,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":436,"s":[100]},{"t":439,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":4,"ix":1}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.5],"t":47,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"t":96,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":47,"op":97,"st":47,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"track matte 2","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Trackpad-JSON_Recents-EDU","tt":1,"tp":5,"refId":"comp_1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":12,"s":[0]},{"t":15,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,282,0],"ix":2,"l":2},"a":{"a":0,"k":[277,282,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":554,"h":564,"ip":12,"op":58,"st":-235,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"track matte 1","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":0,"nm":"Trackpad-JSON_Recents-EDU","tt":1,"tp":7,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,282,0],"ix":2,"l":2},"a":{"a":0,"k":[277,282,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":554,"h":564,"ip":-217,"op":33,"st":-217,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":4,"ix":1}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.5],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"t":49,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":0,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":96,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":221,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}}
\ No newline at end of file
+{"v":"5.12.1","fr":60,"ip":0,"op":97,"w":554,"h":564,"nm":"Trackpad-JSON_Recents-Success","ddd":0,"assets":[{"id":"comp_0","nm":"TrackpadAK_Success_Checkmark","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Check Rotate","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":2,"s":[-16]},{"t":20,"s":[6]}],"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[95.049,95.049,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":228,"st":-72,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Bounce","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":12,"s":[0]},{"t":36,"s":[-6]}],"ix":10},"p":{"a":0,"k":[81,127,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.263,0.263,0.833],"y":[1.126,1.126,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.958,0.958,0]},"t":1,"s":[80,80,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.45,0.45,0.167],"y":[0.325,0.325,0]},"t":20,"s":[105,105,100]},{"t":36,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-0.289,"ix":10},"p":{"a":0,"k":[14.364,-33.591,0],"ix":2,"l":2},"a":{"a":0,"k":[-0.125,0,0],"ix":1,"l":2},"s":{"a":0,"k":[104.744,104.744,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.401,-0.007],[-10.033,11.235]],"o":[[5.954,7.288],[1.401,0.007],[0,0]],"v":[[-28.591,4.149],[-10.73,26.013],[31.482,-21.255]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":3,"s":[0]},{"i":{"x":[0.22],"y":[1]},"o":{"x":[0.001],"y":[0.149]},"t":10,"s":[29]},{"t":27,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":11,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":5,"op":44,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[95,95,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.275,0.275,0.21],"y":[1.102,1.102,1]},"o":{"x":[0.037,0.037,0.05],"y":[0.476,0.476,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.252,0.252,0.47],"y":[0.159,0.159,0]},"t":16,"s":[120,120,100]},{"t":28,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.32,0.32],"y":[0.11,0.11]},"t":16,"s":[148,148]},{"t":28,"s":[136,136]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":88,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Checkbox - Widget","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Trackpad-JSON_Recents-EDU","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":37,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":47,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[100]},{"t":256,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-20,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-20,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}],"ix":1}}]}],"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":248,"s":[33,0],"to":[0,0],"ti":[0,0]},{"t":258,"s":[41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"right circle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[-41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":248,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"t":258,"s":[-41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"left circle","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"size","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":37,"op":345,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Recents_EDU Loop","parent":5,"tt":1,"tp":5,"refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"Recents_EDU Loop","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"CNTL || playback","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":0,"ix":3},"y":{"a":0,"k":0,"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Picker","np":3,"mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ","ix":1,"en":1,"ef":[{"ty":7,"nm":"Menu","mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ-0001","ix":1,"v":{"a":0,"k":2,"ix":1}}]},{"ty":5,"nm":"OUTPUT","np":3,"mn":"ADBE Slider Control","ix":2,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"k":[{"s":[1],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.009],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.038],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.093],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.193],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.4],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.636],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.739],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.8],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.84],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.871],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.894],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.912],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.928],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.94],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.951],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.959],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.967],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.973],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.979],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.983],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.987],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.99],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.993],"t":273,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.995],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.997],"t":275,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.998],"t":276,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.999],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}}]},{"ty":5,"nm":"Keys","np":3,"mn":"ADBE Slider Control","ix":3,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.831],"y":[0.109]},"o":{"x":[0.458],"y":[0.053]},"t":142,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.15],"y":[0.43]},"t":161,"s":[0.15]},{"t":217,"s":[1],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[1]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[1.4]},{"t":280,"s":[2],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[2]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":385,"s":[2.4]},{"t":410,"s":[3]}],"ix":1}}]},{"ty":5,"nm":"State (holds)","np":3,"mn":"ADBE Slider Control","ix":4,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":0,"k":0,"ix":1}}]}],"shapes":[],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"Null :: Taskbar drop","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[252,298.112,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"Taskbar Lofi","parent":3,"refId":"comp_3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":134,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":143,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":432,"s":[100]},{"t":444,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":0,"ix":3},"y":{"k":[{"s":[-10],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-10],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[91,15,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}],"ix":1}}]}],"w":182,"h":30,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":3,"nm":"Focus Task :: Lift & Drop","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":252,"ix":3},"y":{"k":[{"s":[119.5],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.746],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.54],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.071],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.808],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[130.5],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[136.982],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[139.835],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[141.489],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[142.613],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[143.442],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.082],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.593],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.01],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.354],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.642],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.884],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.089],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.262],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.409],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.534],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.638],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.725],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.857],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"matte","parent":5,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"k":[{"s":[302.247,188.904],"t":251,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[301.752,188.595],"t":252,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[300.798,187.999],"t":253,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[299.093,186.933],"t":254,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[295.546,184.716],"t":255,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[291.506,182.192],"t":256,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[289.729,181.08],"t":257,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[288.698,180.436],"t":258,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.998,179.999],"t":259,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.481,179.676],"t":260,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.082,179.427],"t":261,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.764,179.227],"t":262,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.504,179.065],"t":263,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.29,178.931],"t":264,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.11,178.819],"t":265,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.832,178.645],"t":267,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.555,178.472],"t":270,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.272,178.295],"t":278,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0],"ix":3},"r":{"k":[{"s":[14.603],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.579],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.532],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.45],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.278],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.082],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.996],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.946],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.912],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.887],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.868],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.853],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.84],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.83],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.821],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.808],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.794],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.78],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Recents_LofiApp","parent":6,"tt":1,"tp":6,"refId":"comp_4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"k":[{"s":[59.97,59.97,100],"t":251,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.871,59.871,100],"t":252,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.682,59.682,100],"t":253,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.344,59.344,100],"t":254,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[58.64,58.64,100],"t":255,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.839,57.839,100],"t":256,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.486,57.486,100],"t":257,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.281,57.281,100],"t":258,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.142,57.142,100],"t":259,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.04,57.04,100],"t":260,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.961,56.961,100],"t":261,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.898,56.898,100],"t":262,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.846,56.846,100],"t":263,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.804,56.804,100],"t":264,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.768,56.768,100],"t":265,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.713,56.713,100],"t":267,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.658,56.658,100],"t":270,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.602,56.602,100],"t":278,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":7,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"t":277,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,-30.035,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[176.678,176.678,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"second Tasks Zoom back","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":385,"s":[98,98,100]},{"t":410,"s":[95,95,100]}],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Null :: Reposition Side Task","parent":9,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-318.4,-38,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-277.34,-48.1,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,-63.25,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":12,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-111.72,0],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-111.197,0],"t":251,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-109.514,0],"t":252,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-106.268,0],"t":253,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-100.462,0],"t":254,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-88.39,0],"t":255,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-74.643,0],"t":256,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-68.591,0],"t":257,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-65.083,0],"t":258,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-62.7,0],"t":259,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-60.943,0],"t":260,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-59.584,0],"t":261,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-58.5,0],"t":262,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-57.616,0],"t":263,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.886,0],"t":264,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.276,0],"t":265,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.762,0],"t":266,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.328,0],"t":267,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.96,0],"t":268,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.648,0],"t":269,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.385,0],"t":270,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.163,0],"t":271,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.977,0],"t":272,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.824,0],"t":273,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.698,0],"t":274,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.598,0],"t":275,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.521,0],"t":276,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.463,0],"t":277,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.424,0],"t":278,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":10,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.963},"t":217,"s":[-84.8,0,0],"to":[0,0,0],"ti":[0,0,0]},{"t":250,"s":[0,0,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":250,"s":[302.4,189]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":255,"s":[227.56,142.34]},{"t":280,"s":[115.3,72.35]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[14.6]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[14.272]},{"t":280,"s":[13.78]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":14,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-53.175,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.175,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-411.95,20.325,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-333.47,29.183,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,42.47,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[115.3,72.35],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13.78,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0}]},{"id":"comp_3","nm":"Taskbar Lofi","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[76,0,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[76,0,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[167,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7511","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[167,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 5","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[139,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[139,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[139,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7508","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[139,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[111,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[111,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[111,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7507","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[111,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 3","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[83,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[83,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7506","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[83,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 2","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[55,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[55,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7505","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[55,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":90,"ix":10},"p":{"k":[{"s":[36,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"k":[{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-5,-0.5],[5,-0.5]],"c":false}],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-5,-0.5],[5,-0.5]],"c":false}],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"divider","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[-70.349,0.652,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.349,0.652,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[6.826,6.826,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 12","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,9.501],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 12","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 11","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 11","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 5","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[9.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.185,0.148],[0,0],[0,0],[0,0],[-0.086,0.24],[0,0.271],[0.468,0.462],[0.671,0],[0.468,-0.468],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.24,0.086]],"o":[[0,0],[0,0],[0,0],[0.148,-0.185],[0.086,-0.24],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.462,0.462],[0,0.671],[0.468,0.462],[0.271,0],[0.24,-0.086]],"v":[[0.48,0.998],[2.809,3.326],[3.326,2.809],[0.998,0.48],[1.349,-0.157],[1.478,-0.924],[0.776,-2.624],[-0.924,-3.326],[-2.633,-2.624],[-3.326,-0.924],[-2.633,0.785],[-0.924,1.478],[-0.157,1.349]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462],[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462]],"o":[[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462],[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462]],"v":[[0.249,0.259],[-0.924,0.739],[-2.106,0.259],[-2.587,-0.924],[-2.106,-2.097],[-0.924,-2.587],[0.249,-2.097],[0.739,-0.924]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 2","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0.4,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"icon","np":6,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[10.326,10.326],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"icon","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[91,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"k":[{"s":[182,30],"t":217,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[182,30],"t":292,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":32.672,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Taskbar Lofi","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_4","nm":"Recents_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[339.937,151.75,0],"ix":2,"l":2},"a":{"a":0,"k":[339.937,151.75,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.021,-1.766],[0,0],[-2.043,0],[0,0],[1.022,1.767]],"o":[[-1.021,-1.766],[0,0],[-1.022,1.767],[0,0],[2.043,0],[0,0]],"v":[[2.297,-7.675],[-2.297,-7.675],[-9.64,5.025],[-7.343,9],[7.343,9],[9.64,5.025]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":9,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[481.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[457.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[292,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[334,279],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[109,28],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[425.5,208.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[160,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[400,158.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[126,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[251,78.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[334,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[340,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":16,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82,171.125,0],"ix":2,"l":2},"a":{"a":0,"k":[82,171.125,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,177.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,165.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 3","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,171.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 2","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82.5,140.5,0],"ix":2,"l":2},"a":{"a":0,"k":[82,140.938,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Search","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,31.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"header","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,257.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 6","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,245.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,251.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 3","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,64],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,171],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"block","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,96.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,84.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,90.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 1","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app only","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"TrackpadAK_Success_Checkmark","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,198.5,0],"ix":2,"l":2},"a":{"a":0,"k":[95,95,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":190,"h":190,"ip":53,"op":97,"st":53,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"track matte 3","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","tt":1,"tp":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":48,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":54,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":436,"s":[100]},{"t":439,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":4,"ix":1}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.5],"t":47,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"t":96,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":47,"op":97,"st":47,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"track matte 2","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Trackpad-JSON_Recents-EDU","tt":1,"tp":5,"refId":"comp_1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":12,"s":[0]},{"t":15,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,282,0],"ix":2,"l":2},"a":{"a":0,"k":[277,282,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":554,"h":564,"ip":12,"op":58,"st":-235,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"track matte 1","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":0,"nm":"Trackpad-JSON_Recents-EDU","tt":1,"tp":7,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,282,0],"ix":2,"l":2},"a":{"a":0,"k":[277,282,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":554,"h":564,"ip":-217,"op":33,"st":-217,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":4,"ix":1}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.5],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"t":49,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":0,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":96,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":221,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}}
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 22d8013..cef0316 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -35,14 +35,14 @@
<string name="extreme_battery_saver_text" msgid="8455810156739865335">"Ekstreem"</string>
<string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"Outodraai skerm"</string>
<string name="usb_device_permission_prompt" msgid="4414719028369181772">"Gee <xliff:g id="APPLICATION">%1$s</xliff:g> toegang tot <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
- <string name="usb_device_permission_prompt_warn" msgid="2309129784984063656">"Laat <xliff:g id="APPLICATION">%1$s</xliff:g> toe om by <xliff:g id="USB_DEVICE">%2$s</xliff:g> in te gaan?\nOpneemtoestemming is nie aan hierdie program verleen nie, maar dit kan oudio deur hierdie USB-toestel vasvang."</string>
+ <string name="usb_device_permission_prompt_warn" msgid="2309129784984063656">"Laat <xliff:g id="APPLICATION">%1$s</xliff:g> toe om by <xliff:g id="USB_DEVICE">%2$s</xliff:g> in te gaan?\nOpneemtoestemming is nie aan hierdie app verleen nie, maar dit kan oudio deur hierdie USB-toestel vasvang."</string>
<string name="usb_audio_device_permission_prompt_title" msgid="4221351137250093451">"Gee <xliff:g id="APPLICATION">%1$s</xliff:g> toegang tot <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
<string name="usb_audio_device_confirm_prompt_title" msgid="8828406516732985696">"Maak <xliff:g id="APPLICATION">%1$s</xliff:g> oop om <xliff:g id="USB_DEVICE">%2$s</xliff:g> te hanteer?"</string>
- <string name="usb_audio_device_prompt_warn" msgid="2504972133361130335">"Opneemtoestemming is nie aan hierdie program verleen nie, maar dit kan oudio deur hierdie USB-toestel opneem. As jy <xliff:g id="APPLICATION">%1$s</xliff:g> met hierdie toestel gebruik, kan dit verhinder dat jy oproepe, kennisgewings en wekkers hoor."</string>
+ <string name="usb_audio_device_prompt_warn" msgid="2504972133361130335">"Opneemtoestemming is nie aan hierdie app verleen nie, maar dit kan oudio deur hierdie USB-toestel opneem. As jy <xliff:g id="APPLICATION">%1$s</xliff:g> met hierdie toestel gebruik, kan dit verhinder dat jy oproepe, kennisgewings en wekkers hoor."</string>
<string name="usb_audio_device_prompt" msgid="7944987408206252949">"As jy <xliff:g id="APPLICATION">%1$s</xliff:g> met hierdie toestel gebruik, kan dit verhinder dat jy oproepe, kennisgewings en wekkers hoor."</string>
<string name="usb_accessory_permission_prompt" msgid="717963550388312123">"Gee <xliff:g id="APPLICATION">%1$s</xliff:g> toegang tot <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="4091711472439910809">"Hanteer <xliff:g id="USB_DEVICE">%2$s</xliff:g> met <xliff:g id="APPLICATION">%1$s</xliff:g>?"</string>
- <string name="usb_device_confirm_prompt_warn" msgid="990208659736311769">"Maak <xliff:g id="APPLICATION">%1$s</xliff:g> oop om <xliff:g id="USB_DEVICE">%2$s</xliff:g> te hanteer?\nOpneemtoestemming is nie aan hierdie program verleen nie, maar dit kan oudio deur hierdie USB-toestel vasvang."</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="990208659736311769">"Maak <xliff:g id="APPLICATION">%1$s</xliff:g> oop om <xliff:g id="USB_DEVICE">%2$s</xliff:g> te hanteer?\nOpneemtoestemming is nie aan hierdie app verleen nie, maar dit kan oudio deur hierdie USB-toestel vasvang."</string>
<string name="usb_accessory_confirm_prompt" msgid="5728408382798643421">"Hanteer <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> met <xliff:g id="APPLICATION">%1$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="6756649383432542382">"Geen geïnstalleerde programme werk met hierdie USB-toebehoorsel nie. Vind meer uit oor hierdie toebehoorsel by <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="1236358027511638648">"USB-toebehoorsel"</string>
@@ -83,7 +83,7 @@
<string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"Toestel moet ontsluit word voordat skermkiekie gestoor kan word"</string>
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Probeer weer skermkiekie neem"</string>
<string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"Kan nie skermkiekie stoor nie"</string>
- <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Die program of jou organisasie laat nie toe dat skermkiekies geneem word nie"</string>
+ <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Die app of jou organisasie laat nie toe dat skermkiekies geneem word nie"</string>
<string name="screenshot_blocked_by_admin" msgid="5486757604822795797">"Die neem van skermskote word deur jou IT-admin geblokkeer"</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"Wysig"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"Wysig skermkiekie"</string>
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Om ’n app met ’n legstuk oop te maak, sal jy moet verifieer dat dit jy is. Hou ook in gedagte dat enigeen dit kan bekyk, selfs wanneer jou tablet gesluit is. Sommige legstukke is moontlik nie vir jou sluitskerm bedoel nie en dit kan onveilig wees om dit hier by te voeg."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Het dit"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Legstukke"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"As jy legstukke as ’n kortpad op die sluitskerm wil byvoeg, moet jy seker maak dit is in instellings geaktiveer."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Wissel gebruiker"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aftrekkieslys"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle programme en data in hierdie sessie sal uitgevee word."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Kennisgewings"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Gesprekke"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Vee alle stil kennisgewings uit"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Kennisgewings onderbreek deur Moenie Steur Nie"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Geen kennisgewings nie}=1{Kennisgewings is deur {mode} onderbreek}=2{Kennisgewings is deur {mode} en een ander modus onderbreek}other{Kennisgewings is deur {mode} en # ander modusse onderbreek}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Begin nou"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Kopnasporing"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tik om luiermodus te verander"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"luiermodus"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"demp"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ontdemp"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibreer"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Skakel oor na app links of bo terwyl jy verdeelde skerm gebruik"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Tydens verdeelde skerm: verplaas ’n app van een skerm na ’n ander"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Skuif aktiewe venster tussen skerms"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Skuif venster na links"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Skuif venster na regs"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maak venster groot"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Maak venster klein"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Invoer"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Skakel oor na volgende taal"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Skakel oor na vorige taal"</string>
@@ -996,7 +999,7 @@
<string name="tuner_left" msgid="5758862558405684490">"Links"</string>
<string name="tuner_right" msgid="8247571132790812149">"Regs"</string>
<string name="tuner_menu" msgid="363690665924769420">"Kieslys"</string>
- <string name="tuner_app" msgid="6949280415826686972">"<xliff:g id="APP">%1$s</xliff:g>-program"</string>
+ <string name="tuner_app" msgid="6949280415826686972">"<xliff:g id="APP">%1$s</xliff:g>-app"</string>
<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>
@@ -1007,8 +1010,8 @@
<string name="notification_channel_accessibility" msgid="8956203986976245820">"Toeganklikheid"</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>
+ <string name="instant_apps_message" msgid="6112428971833011754">"App is oopgemaak sonder dat dit geïnstalleer is."</string>
+ <string name="instant_apps_message_with_help" msgid="1816952263531203932">"App is oopgemaak sonder dat dit geïnstalleer is. Tik om meer inligting te kry."</string>
<string name="app_info" msgid="5153758994129963243">"Appinligting"</string>
<string name="go_to_web" msgid="636673528981366511">"Gaan na blaaier"</string>
<string name="mobile_data" msgid="4564407557775397216">"Mobiele data"</string>
@@ -1019,8 +1022,8 @@
<string name="dnd_is_off" msgid="3185706903793094463">"Moenie Steur Nie is af"</string>
<string name="dnd_is_on" msgid="7009368176361546279">"Moenie Steur Nie is aan"</string>
<string name="qs_dnd_prompt_auto_rule" msgid="3535469468310002616">"\'n Outomatiese reël (<xliff:g id="ID_1">%s</xliff:g>) het Moenie Steur Nie aangeskakel."</string>
- <string name="qs_dnd_prompt_app" msgid="4027984447935396820">"\'n Program (<xliff:g id="ID_1">%s</xliff:g>) het Moenie Steur Nie aangeskakel."</string>
- <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"\'n Outomatiese reël of program het Moenie Steur Nie aangeskakel."</string>
+ <string name="qs_dnd_prompt_app" msgid="4027984447935396820">"\'n App (<xliff:g id="ID_1">%s</xliff:g>) het Moenie Steur Nie aangeskakel."</string>
+ <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"\'n Outomatiese reël of app het Moenie Steur Nie aangeskakel."</string>
<string name="running_foreground_services_title" msgid="5137313173431186685">"Programme wat op die agtergrond loop"</string>
<string name="running_foreground_services_msg" msgid="3009459259222695385">"Tik vir besonderhede oor battery- en datagebruik"</string>
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Skakel mobiele data af?"</string>
@@ -1030,11 +1033,11 @@
<string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobiele data sal nie outomaties op grond van beskikbaarheid oorskakel nie"</string>
<string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Nee, dankie"</string>
<string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Ja, skakel oor"</string>
- <string name="touch_filtered_warning" msgid="8119511393338714836">"Instellings kan nie jou antwoord verifieer nie omdat \'n program \'n toestemmingversoek verberg."</string>
+ <string name="touch_filtered_warning" msgid="8119511393338714836">"Instellings kan nie jou antwoord verifieer nie omdat \'n app \'n toestemmingversoek verberg."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Laat <xliff:g id="APP_0">%1$s</xliff:g> toe om <xliff:g id="APP_2">%2$s</xliff:g>-skyfies te wys?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"– Dit kan inligting van <xliff:g id="APP">%1$s</xliff:g> af lees"</string>
<string name="slice_permission_text_2" msgid="6758906940360746983">"– Dit kan handelinge binne <xliff:g id="APP">%1$s</xliff:g> uitvoer"</string>
- <string name="slice_permission_checkbox" msgid="4242888137592298523">"Laat <xliff:g id="APP">%1$s</xliff:g> toe om skyfies uit enige program te gebruik"</string>
+ <string name="slice_permission_checkbox" msgid="4242888137592298523">"Laat <xliff:g id="APP">%1$s</xliff:g> toe om skyfies uit enige app te gebruik"</string>
<string name="slice_permission_allow" msgid="6340449521277951123">"Laat toe"</string>
<string name="slice_permission_deny" msgid="6870256451658176895">"Weier"</string>
<string name="auto_saver_title" msgid="6873691178754086596">"Tik om Batterybespaarder te skeduleer"</string>
@@ -1204,8 +1207,8 @@
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(ontkoppel)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Kan nie wissel nie. Tik om weer te probeer."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Koppel ’n toestel"</string>
- <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Maak die program oop om hierdie sessie uit te saai."</string>
- <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Onbekende program"</string>
+ <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Maak die app oop om hierdie sessie uit te saai."</string>
+ <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Onbekende app"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Hou op uitsaai"</string>
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Beskikbare toestelle vir oudio-uitsette."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Stelselkontroles"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Stelselapps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Verrigting van veelvuldige take"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Onlangse apps"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Verdeelde skerm"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Invoer"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Appkortpaaie"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Kortpadsleutels"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Pasmaak kortpadsleutels"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Verwyder kortpad?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Druk sleutel om kortpad toe te wys"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Dit sal jou gepasmaakte kortpad permanent uitvee."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Soekkortpaaie"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Geen soekresultate nie"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Vou ikoon in"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikoon vir Handeling- of Meta-sleutel"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plusikoon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Pasmaak"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Klaar"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vou ikoon uit"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"vorentoe-skuinsstreep"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Sleephandvatsel"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Sleutelbordinstellings"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Stel kortpad"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Verwyder"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Kanselleer"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Druk sleutel"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Sleutelkombinasie is reeds in gebruik. Probeer ’n ander sleutel."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Druk die handelingsleutel op jou sleutelbord"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Welgedaan!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Jy het die Bekyk Onlangse Apps-gebaar voltooi"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Sleutelbordlig"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Vlak %1$d van %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Huiskontroles"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index e36aab2..5f77743 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ምግብር በመጠቀም መተግበሪያ ለመክፈት እርስዎ መሆንዎን ማረጋገጥ አለብዎት። እንዲሁም የእርስዎ ጡባዊ በተቆለፈበት ጊዜ እንኳን ማንኛውም ሰው እነሱን ማየት እንደሚችል ከግምት ውስጥ ያስገቡ። አንዳንድ ምግብሮች ለማያ ገፅ ቁልፍዎ የታሰቡ ላይሆኑ ይችላሉ እና እዚህ ለማከል አስተማማኝ ላይሆኑ ይችላሉ።"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ገባኝ"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ምግብሮች"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"ማያ ገጽ ቁልፍ ላይ ምግብሮችን እንደ አቋራጭ ለማከል በቅንብሮች ውስጥ መንቃቱን ያረጋግጡ።"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"የ«ምግብሮች» አቋራጭን ለማከል በቅንብሮች ውስጥ «ምግብሮችን በማያ ገፅ ቁልፍ ላይ አሳይ» የሚለው መንቃቱን ያረጋግጡ።"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ቅንብሮች"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ተጠቃሚ ቀይር"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ወደታች ተጎታች ምናሌ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"በዚህ ክፍለ-ጊዜ ውስጥ ያሉ ሁሉም መተግበሪያዎች እና ውሂብ ይሰረዛሉ።"</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ማሳወቂያዎች"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"ውይይቶች"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ሁሉንም ጸጥ ያሉ ማሳወቂያዎችን ያጽዱ"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"ማሳወቂያዎች በአትረብሽ ባሉበት ቆመዋል"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{ምንም ማሳወቂያዎች የሉም}=1{ማሳወቂያዎች በ{mode} ባሉበት ቆመዋል}=2{ማሳወቂያዎች በ{mode} እና አንድ ሌላ ሁነታ ባሉበት ቆመዋል}one{ማሳወቂያዎች በ{mode} እና # ሌላ ሁነታ ባሉበት ቆመዋል}other{ማሳወቂያዎች በ{mode} እና # ሌላ ሁነታዎች ባሉበት ቆመዋል}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"አሁን ጀምር"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"የጭንቅላት ክትትል"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"የደዋይ ሁነታን ለመቀየር መታ ያድርጉ"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ደዋይ ሁነታ"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ድምጸ-ከል አድርግ"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ድምጸ-ከልን አንሳ"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ንዘር"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"የተከፈለ ማያ ገጽን ሲጠቀሙ በቀኝ ወይም ከላይ ወዳለ መተግበሪያ ይቀይሩ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"በተከፈለ ማያ ገጽ ወቅት፡- መተግበሪያን ከአንዱ ወደ ሌላው ተካ"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"በማሳያዎች መካከል ንቁ መስኮትን ያንቀሳቅሱ"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"መስኮትን ወደ ግራ አሳንስ"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"መስኮትን ወደ ቀኝ አሳንስ"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"መስኮትን አሳድግ"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"መስኮት አሳንስ"</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>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"የሥርዓት መቆጣጠሪያዎች"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"የሥርዓት መተግበሪያዎች"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ብዙ ተግባራትን በተመሳሳይ ጊዜ ማከናወን"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"የቅርብ ጊዜ መተግበሪያዎች"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"የተከፈለ ማያ ገፅ"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ግብዓት"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"የመተግበሪያ አቋራጮች"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"የቁልፍ ሰሌዳ አቋራጮች"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"የቁልፍ ሰሌዳ አቋራጮችን ያብጁ"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"አቋራጭ ይወገድ?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ወደ ነባሪ ዳግም ይጀመር?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"አቋራጭ ለመመደብ ቁልፍ ይጫኑ"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ይህ ብጁ አቋራጭዎን በቋሚነት ይሰርዛል።"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ይህ ሁሉንም ብጁ አቋራጮችዎን በቋሚነት ይሰርዛል።"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"የፍለጋ አቋራጮች"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ምንም የፍለጋ ውጤቶች የሉም"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"መሰብሰቢያ አዶ"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"የእርምጃ ወይም ሜታ ቁልፍ አዶ"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"የመደመር አዶ"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"አብጅ"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ተከናውኗል"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"መዘርጊያ አዶ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ወይም"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"ሲደመር"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ወደፊት ህዝባር"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"መያዣ ይጎትቱ"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"የቁልፍ ሰሌዳ ቅንብሮች"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"አቋራጭ አቀናብር"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"አስወግድ"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"አዎ፣ ዳግም አስጀምር"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ይቅር"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ቁልፍ ይጫኑ"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"የቁልፍ ጥምረት አስቀድሞ በሥራ ላይ ነው። ሌላ ቁልፍ ይሞክሩ።"</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"በቁልፍ ሰሌዳዎ ላይ ያለውን የተግባር ቁልፍ ይጫኑ"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ጥሩ ሠርተዋል!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"የሁሉንም መተግበሪያዎች አሳይ ምልክትን አጠናቅቀዋል"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index d6e91cc..dd6e850 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"لفتح تطبيق باستخدام تطبيق مصغَّر، عليك إثبات هويتك. يُرجى ملاحظة أنّ أي شخص يمكنه الاطّلاع محتوى التطبيقات المصغَّرة، حتى وإن كان جهازك اللوحي مُقفلاً. بعض التطبيقات المصغّرة قد لا تكون مُصمَّمة لإضافتها إلى شاشة القفل، وقد يكون هذا الإجراء غير آمن."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"حسنًا"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"التطبيقات المصغَّرة"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"لإضافة التطبيقات المصغّرة على شاشة القفل كاختصار، تأكَّد من تفعيلها في الإعدادات."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"لإضافة اختصار \"التطبيقات المصغّرة\"، يجب تفعيل خيار \"عرض التطبيقات المصغّرة على شاشة القفل\" من خلال الإعدادات."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"الإعدادات"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تبديل المستخدم"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"القائمة المنسدلة"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"سيتم حذف كل التطبيقات والبيانات في هذه الجلسة."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"الإشعارات"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"المحادثات"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"محو جميع الإشعارات الصامتة"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"تم إيقاف الإشعارات مؤقتًا وفقًا لإعداد \"عدم الإزعاج\""</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{ما مِن إشعارات}=1{تم إيقاف الإشعارات مؤقتًا بواسطة \"{mode}\"}=2{تم إيقاف الإشعارات مؤقتًا بواسطة \"{mode}\" ووضع واحد آخر}few{تم إيقاف الإشعارات مؤقتًا بواسطة \"{mode}\" و# أوضاع أخرى}many{تم إيقاف الإشعارات مؤقتًا بواسطة \"{mode}\" و# وضعًا آخر}other{تم إيقاف الإشعارات مؤقتًا بواسطة \"{mode}\" و# وضع آخر}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"البدء الآن"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"تتبُّع حركة الرأس"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"انقر لتغيير وضع الرنين."</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"وضع الرنين"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"كتم الصوت"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"إعادة الصوت"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"اهتزاز"</string>
@@ -1415,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"عناصر التحكّم في النظام"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"تطبيقات النظام"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"تعدُّد المهام"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"التطبيقات المستخدمة مؤخرًا"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"تقسيم الشاشة"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"الإدخال"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"اختصارات التطبيقات"</string>
@@ -1424,14 +1428,18 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"اختصارات لوحة المفاتيح"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"تخصيص اختصارات لوحة المفاتيح"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"هل تريد إزالة هذا الاختصار؟"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"يُرجى تأكيد إعادة الضبط على الإعدادات التلقائية"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"اضغط على مفتاح لتخصيص الاختصار"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"سيؤدي هذا الإجراء إلى حذف الاختصار المخصّص نهائيًا."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"سيؤدي هذا الإجراء إلى حذف جميع الاختصارات المخصّصة نهائيًا."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"البحث في الاختصارات"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ما مِن نتائج بحث"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"رمز التصغير"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"رمز مفتاح الإجراء (مفتاح Meta)"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"رمز علامة الجمع (+)"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"تخصيص"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"تم"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"رمز التوسيع"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"أو"</string>
@@ -1441,6 +1449,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"إعدادات لوحة المفاتيح"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ضبط الاختصار"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"إزالة"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"نعم، أريد إعادة الضبط"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"إلغاء"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"اضغط على مفتاح"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"يتم حاليًا استخدام مجموعة المفاتيح هذه. يُرجى تجربة مفتاح آخر."</string>
@@ -1472,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"اضغط على مفتاح الإجراء في لوحة المفاتيح"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"أحسنت!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"لقد أكملْت التدريب على إيماءة عرض جميع التطبيقات"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 8f5b1e1..86e13055 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"এটা ৱিজেট ব্যৱহাৰ কৰি কোনো এপ্ খুলিবলৈ, এয়া আপুনিয়েই বুলি সত্যাপন পৰীক্ষা কৰিব লাগিব। লগতে, মনত ৰাখিব যে যিকোনো লোকেই সেইবোৰ চাব পাৰে, আনকি আপোনাৰ টেবলেটটো লক হৈ থাকিলেও। কিছুমান ৱিজেট হয়তো আপোনাৰ লক স্ক্ৰীনৰ বাবে কৰা হোৱা নাই আৰু ইয়াত যোগ কৰাটো অসুৰক্ষিত হ’ব পাৰে।"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"বুজি পালোঁ"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ৱিজেট"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"লক স্ক্ৰীনৰ ৱিজেট সুবিধাটো শ্বৰ্টকাট হিচাপে যোগ দিবলৈ ছেটিঙত সেয়া সক্ষম হৈ থকাটো নিশ্চিত কৰক।"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"ৱিজেট\"ৰ শ্বৰ্টকাট যোগ দিবলৈ, ছেটিঙত \"লক স্ক্ৰীনত ৱিজেট দেখুৱাওক\" সক্ষম কৰি থোৱাটো নিশ্চিত কৰক।"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ছেটিং"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যৱহাৰকাৰী সলনি কৰক"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"পুল-ডাউনৰ মেনু"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই ছেশ্বনৰ আটাইবোৰ এপ্ আৰু ডেটা মচা হ\'ব।"</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"জাননীসমূহ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"বাৰ্তালাপ"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"আটাইবোৰ নীৰৱ জাননী মচক"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"অসুবিধা নিদিব-ই জাননী পজ কৰিছে"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{কোনো জাননী নাই}=1{{mode}এ জাননী পজ কৰিছে}=2{{mode} আৰু আন এটা ম’ডে জাননী পজ কৰিছে}one{{mode} আৰু আন # টা ম’ডে জাননী পজ কৰিছে}other{{mode} আৰু আন # টা ম’ডে জাননী পজ কৰিছে}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"এতিয়াই আৰম্ভ কৰক"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"হে’ড ট্ৰেকিং"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ৰিংগাৰ ম’ড সলনি কৰিবলৈ টিপক"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ৰিংগাৰ ম’ড"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"মিউট কৰক"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"আনমিউট কৰক"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"কম্পন কৰক"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰাৰ সময়ত বাওঁফালে অথবা ওপৰত থকা এপলৈ সলনি কৰক"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"বিভাজিত স্ক্ৰীনৰ ব্যৱহাৰ কৰাৰ সময়ত: কোনো এপ্ এখন স্ক্ৰীনৰ পৰা আনখনলৈ নিয়ক"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"ডিছপ্লে’সমূহৰ মাজত সক্রিয় হৈ থকা ৱিণ্ড’ সলনা সলনিকৈ ব্যৱহাৰ কৰক"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"ৱিণ্ড’ বাওঁফাললৈ স্থানান্তৰ কৰক"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"ৱিণ্ড’ সোঁফাললৈ স্থানান্তৰ কৰক"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"ৱিণ্ড’ মেক্সিমাইজ কৰক"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"ৱিণ্ড’ মিনিমাইজ কৰক"</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>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"ছিষ্টেমৰ নিয়ন্ত্ৰণ"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"ছিষ্টেম এপ্"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"মাল্টিটাস্কিং"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"শেহতীয়া এপ্সমূহ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"বিভাজিত স্ক্ৰীন"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ইনপুট"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"এপ্ শ্বৰ্টকাটসমূহ"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"কীব’ৰ্ডৰ শ্বৰ্টকাট"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"কীব’ৰ্ডৰ শ্বৰ্টকাট কাষ্টমাইজ কৰক"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"শ্বৰ্টকাট আঁতৰাবনে?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ডিফ\'ল্ট হিচাপে পুনৰ ৰিছেট কৰিবনে?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"শ্বৰ্টকাটৰ ভূমিকা অৰ্পণ কৰিবলৈ কী টিপক"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"এইটোৱে আপোনাৰ কাষ্টম শ্বৰ্টকাট মচিব।"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"এইটোৱে আপোনাৰ আটাইবোৰ কাষ্টম শ্বৰ্টকাট স্থায়ীভাৱে মচিব।"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"সন্ধানৰ শ্বৰ্টকাট"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"সন্ধানৰ কোনো ফলাফল নাই"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"সংকোচন কৰাৰ চিহ্ন"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"কাৰ্য বা মেটা কীৰ চিহ্ন"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"যোগ চিনৰ চিহ্ন"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"কাষ্টমাইজ কৰক"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"হ’ল"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"বিস্তাৰ কৰাৰ চিহ্ন"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"যোগ চিন"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ফৰৱাৰ্ড শ্লেশ্ব"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ড্ৰেগ হেণ্ডেল"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"কীব’ৰ্ডৰ ছেটিং"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"শ্বৰ্টকাট ছেট কৰক"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"আঁতৰাওক"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"হয়, ৰিছেট কৰক"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"বাতিল কৰক"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"কী টিপক"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"কীৰ মিশ্ৰণ ইতিমধ্যে ব্যৱহাৰ হৈ আছে। অন্য এটা কী ব্যৱহাৰ কৰি চাওক।"</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"আপোনাৰ কীব’ৰ্ডৰ কাৰ্য কীটোত টিপক"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"বঢ়িয়া!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"আপুনি আটাইবোৰ এপ্ চোৱাৰ নিৰ্দেশনাটো সম্পূৰ্ণ কৰিছে"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 1126da1..3082284 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Vidcetdən istifadə edərək tətbiqi açmaq üçün kimliyi doğrulamalısınız. Planşet kilidli olsa da, hər kəs vidcetlərə baxa bilər. Bəzi vidcetlər kilid ekranı üçün nəzərdə tutulmayıb və bura əlavə etmək təhlükəli ola bilər."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Anladım"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Vidcetlər"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Kilidli ekrana qısayol kimi Vidcet əlavə etmək üçün onun ayarlarda aktiv olduğundan əmin olun."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aşağı çəkilən menyu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu sessiyada bütün tətbiqlər və data silinəcək."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Bildirişlər"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Söhbətlər"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Səssiz bildirişlərin hamısını silin"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Bildirişlər \"Narahat Etməyin\" rejimi tərəfindən dayandırıldı"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Bildiriş yoxdur}=1{Bildirişlər {mode} tərəfindən dayandırıldı}=2{Bildirişlər {mode} və digər rejim tərəfindən dayandırıldı}other{Bildirişlər {mode} və # digər rejim tərəfindən dayandırıldı}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"İndi başlayın"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Baş izləməsi"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Zəng rejimini dəyişmək üçün toxunun"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"zəng səsi rejimi"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"susdurun"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"səssiz rejimdən çıxarın"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrasiya"</string>
@@ -875,14 +882,10 @@
<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>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Bölünmüş ekran rejimində: tətbiqi birindən digərinə dəyişin"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Aktiv pəncərəni displeylər arasında hərəkət etdirin"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Pəncərəni sola hərəkət etdirin"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Pəncərəni sağa hərəkət etdirin"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Pəncərəni böyüdün"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Pəncərəni minimallaşdırın"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Daxiletmə"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Növbəti dilə keçin"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Əvvəlki dilə keçin"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Sistem nizamlayıcıları"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistem tətbiqləri"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Çoxsaylı tapşırıq icrası"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Son tətbiqlər"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Bölünmüş ekran"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Daxiletmə"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Tətbiq qısayolları"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatura qısayolları"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Klaviatura qısayollarını fərdiləşdirin"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Qısayol silinsin?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Qısayol təyin etmək üçün düyməni basın"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Bu, fərdi qısayolunuzu həmişəlik siləcək."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Axtarış qısayolları"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Axtarış nəticəsi yoxdur"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"İkonanı yığcamlaşdırın"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Əməliyyat və ya Meta düyməsi ikonası"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Üstəgəl ikonası"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Fərdiləşdirin"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Hazırdır"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"İkonanı genişləndirin"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"və ya"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"irəli sləş"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Dəstəyi çəkin"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatura ayarları"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Qısayol ayarlayın"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Silin"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Ləğv edin"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Düyməni basın"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Düymə kombinasiyası artıq istifadə olunur. Başqa düyməni sınayın."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Klaviaturada fəaliyyət açarına basın"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Əla!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"\"Bütün tətbiqlərə baxın\" jestini tamamladınız"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura işığı"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Səviyyə %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Ev nizamlayıcıları"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index a996c8a..09aef06 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da biste otvorili aplikaciju koja koristi vidžet, treba da potvrdite da ste to vi. Imajte u vidu da svako može da ga vidi, čak i kada je tablet zaključan. Neki vidžeti možda nisu namenjeni za zaključani ekran i možda nije bezbedno da ih tamo dodate."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Važi"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Vidžeti"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Da biste dodali vidžete na zaključani ekran kao prečicu, uverite se da je to omogućeno u podešavanjima."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Da biste dodali prečicu Vidžeti, uverite se da je u podešavanjima omogućeno Prikazuj vidžete na zaključanom ekranu."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Podešavanja"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zameni korisnika"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući meni"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji će biti izbrisani."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Obaveštenja"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Konverzacije"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Obrišite sva nečujna obaveštenja"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Obaveštenja su pauzirana režimom Ne uznemiravaj"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nema obaveštenja}=1{Obaveštenja je pauzirao {mode}}=2{Obaveštenja su pauzirali {mode} i još jedan režim}one{Obaveštenja su pauzirali {mode} i još # režim}few{Obaveštenja su pauzirali {mode} i još # režima}other{Obaveštenja su pauzirali {mode} i još # režima}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Započni"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Praćenje glave"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Dodirnite da biste promenili režim zvona"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"režim zvona"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključite zvuk"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključite zvuk"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibracija"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Pređite u aplikaciju sleva ili iznad dok koristite podeljeni ekran"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"U režimu podeljenog ekrana: zamena jedne aplikacije drugom"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Premesti aktivan prozor na sledeći ekran"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Pomerite prozor nalevo"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Pomerite prozor nadesno"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Povećajte prozor"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Smanjite prozor"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Unos"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Pređi na sledeći jezik"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Pređi na prethodni jezik"</string>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Sistemske kontrole"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistemske aplikacije"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Obavljanje više zadataka istovremeno"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Nedavne aplikacije"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Podeljeni ekran"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Unos"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Prečice za aplikacije"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tasterske prečice"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prilagodite tasterske prečice"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Želite da uklonite prečicu?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Želite da resetujete na podrazumevano?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Pritisnite taster da biste dodelili prečicu"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ovim ćete trajno izbrisati prilagođenu prečicu."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Time ćete trajno izbrisati sve prilagođene prečice."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pretražite prečice"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nema rezultata pretrage"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za skupljanje"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona tastera za radnju ili meta tastera"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona znaka plus"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagodi"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotovo"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"kosa crta unapred"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Marker za prevlačenje"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Podešavanja tastature"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Podesi prečicu"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Ukloni"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Da, resetuj"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Otkaži"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pritisnite taster"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinacija tastera se već koristi. Probajte sa drugim tasterom."</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite taster radnji na tastaturi"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Odlično!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Dovršili ste pokret za prikazivanje svih aplikacija."</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvetljenje tastature"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index c14d1b5..51cc35a 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Каб адкрыць праграму з дапамогай віджэта, вам неабходна будзе пацвердзіць сваю асобу. Таксама памятайце, што такія віджэты могуць пабачыць іншыя людзі, нават калі экран планшэта заблакіраваны. Некаторыя віджэты могуць не падыходзіць для выкарыстання на экране блакіроўкі, і дадаваць іх сюды можа быць небяспечна."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Зразумела"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Віджэты"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Каб можна было дадаць віджэты на экран блакіроўкі ў якасці спалучэння клавіш, яны павінны быць уключаны ў наладах."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Каб дадаць спалучэнне клавіш \"Віджэты\", у наладах павінна быць уключана функцыя \"Паказваць віджэты на экране блакіроўкі\"."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Налады"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Перайсці да іншага карыстальніка"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"высоўнае меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усе праграмы і даныя гэтага сеанса будуць выдалены."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Апавяшчэнні"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Размовы"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Выдаліць усе апавяшчэнні без гуку"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Паказ апавяшчэнняў прыпынены ў рэжыме \"Не турбаваць\""</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Апавяшчэнняў няма}=1{Атрыманне апавяшчэнняў прыпынена рэжымам \"{mode}\"}=2{Атрыманне апавяшчэнняў прыпынена рэжымам \"{mode}\" і яшчэ адным рэжымам}one{Атрыманне апавяшчэнняў прыпынена рэжымам \"{mode}\" і яшчэ # рэжымам}few{Атрыманне апавяшчэнняў прыпынена рэжымам \"{mode}\" і яшчэ # рэжымамі}many{Атрыманне апавяшчэнняў прыпынена рэжымам \"{mode}\" і яшчэ # рэжымамі}other{Атрыманне апавяшчэнняў прыпынена рэжымам \"{mode}\" і яшчэ # рэжыму}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Пачаць зараз"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Адсочваць рух галавы"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Націсніце, каб змяніць рэжым званка"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"рэжым званка"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"выключыць гук"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"уключыць гук"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вібрыраваць"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Пераключыцца на праграму злева або ўверсе на падзеленым экране"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"У рэжыме падзеленага экрана замяніць адну праграму на іншую"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Перамясціць актыўнае акно паміж дысплэямі"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Перамясціць акно ўлева"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Перамясціць акно ўправа"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Разгарнуць акно"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Згарнуць акно"</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>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Элементы кіравання сістэмай"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Сістэмныя праграмы"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Шматзадачнасць"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Нядаўнія праграмы"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Падзелены экран"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Увод"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Ярлыкі праграм"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Спалучэнні клавіш"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Наладзіць спалучэнні клавіш"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Выдаліць спалучэнне клавіш?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Скінуць налады да стандартных?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Націсніце клавішу, каб прызначыць спалучэнне клавіш"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Гэта дзеянне назаўсёды выдаліць прызначанае вамі спалучэнне клавіш."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Усе карыстальніцкія спалучэнні клавіш будуць назаўсёды выдалены."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пошук спалучэнняў клавіш"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Няма вынікаў пошуку"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Згарнуць\""</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Значок клавішы дзеяння (мета-клавішы)"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Значок плюса"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Наладзіць"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Гатова"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Разгарнуць\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"+"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"касая рыса ўправа"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер перацягвання"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Налады клавіятуры"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Наладзіць спалучэнне клавіш"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Выдаліць"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Так, скінуць"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Скасаваць"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Націсніце клавішу"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Гэта спалучэнне клавіш ужо выкарыстоўваецца. Паспрабуйце іншую клавішу."</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Націсніце клавішу дзеяння на клавіятуры"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Выдатна!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Вы навучыліся рабіць жэст для прагляду ўсіх праграм"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 73de179..12e25cc 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"За да отворите дадено приложение посредством приспособление, ще трябва да потвърдите, че това сте вие. Също така имайте предвид, че всеки ще вижда приспособленията дори когато таблетът ви е заключен. Възможно е някои от тях да не са предназначени за заключения екран и добавянето им на него може да е опасно."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Разбрах"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Приспособления"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"За да добавите приспособления към заключения екран като пряк път, уверете се, че са активирани в настройките."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Превключване между потребителите"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"падащо меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Всички приложения и данни в тази сесия ще бъдат изтрити."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Известия"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Разговори"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Изчистване на всички беззвучни известия"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Известията са поставени на пауза от режима „Не безпокойте“"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Няма известия}=1{Известията са поставени на пауза от {mode}}=2{Известията са поставени на пауза от {mode} и един друг режим}other{Известията са поставени на пауза от {mode} и # други режима}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Стартиране сега"</string>
@@ -704,6 +709,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Прослед. на движенията на главата"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Докоснете, за да промените режима на звънене"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"режим на звънене"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>: докоснете, за да промените режима на звънене"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"спиране"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"пускане"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибриране"</string>
@@ -875,14 +881,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Превключване към приложението вляво/отгоре в режима на разделен екран"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"При разделен екран: замяна на дадено приложение с друго"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Преместване на активния прозорец между екраните"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Преместване на прозореца наляво"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Преместване на прозореца надясно"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Увеличаване на прозореца"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Намаляване на прозореца"</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>
@@ -1419,7 +1421,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Системни контроли"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системни приложения"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Няколко задачи едновременно"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Скорошни приложения"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Разделен екран"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Въвеждане"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Преки пътища към приложения"</string>
@@ -1428,25 +1429,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Клавишни комбинации"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Персонализиране на клавишните комбинации"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Да се премахне ли клавишната комбинация?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Натиснете клавиш, за да зададете клавишна комбинация"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Това ще изтрие персонализираната клавишна комбинация за постоянно."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Търсете клавишни комбинации"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Няма резултати от търсенето"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за свиване"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Икона на клавиша за действия или клавиша Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Икона на плюс"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Персонализиране"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за разгъване"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"плюс"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"наклонена черта"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Манипулатор за преместване с плъзгане"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Настройки на клавиатурата"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Задаване на клавишна комбинация"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Премахване"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Отказ"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Натиснете клавиш"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Клавишната комбинация вече се използва. Опитайте с друг клавиш."</string>
@@ -1478,6 +1485,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Натиснете клавиша за действия на клавиатурата си"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Браво!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Изпълнихте жеста за преглед на всички приложения"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Анимация за урока. Кликнете, за да поставите на пауза и да възобновите възпроизвеждането."</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>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index edbf73e..8af32a8 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"উইজেট ব্যবহার করে কোনও অ্যাপ খুলতে, আপনাকে নিজের পরিচয় যাচাই করতে হবে। এছাড়াও, মনে রাখবেন, আপনার ট্যাবলেট লক থাকলেও যেকেউ তা দেখতে পারবেন। কিছু উইজেট আপনার লক স্ক্রিনের উদ্দেশ্যে তৈরি করা হয়নি এবং এখানে যোগ করা নিরাপদ নাও হতে পারে।"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"বুঝেছি"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"উইজেট"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"শর্টকাট হিসেবে লক স্ক্রিনে উইজেট যোগ করলে, সেটি সেটিংস থেকে চালু আছে কিনা ভালো করে দেখে নিন।"</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যবহারকারী পাল্টে দিন"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"পুলডাউন মেনু"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই সেশনের সব অ্যাপ ও ডেটা মুছে ফেলা হবে।"</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"বিজ্ঞপ্তি"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"কথোপকথন"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"সব নীরব বিজ্ঞপ্তি মুছুন"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'বিরক্ত করবে না\' দিয়ে বিজ্ঞপ্তি পজ করা হয়েছে"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{কোনও বিজ্ঞপ্তি নেই}=1{{mode}-এর জন্য বিজ্ঞপ্তি পজ করা হয়েছে}=2{{mode} ও অন্য আরেকটি মোডের জন্য বিজ্ঞপ্তি পজ করা হয়েছে}one{{mode} ও অন্য #টি মোডের জন্য বিজ্ঞপ্তি পজ করা হয়েছে}other{{mode} ও অন্য #টি মোডের জন্য বিজ্ঞপ্তি পজ করা হয়েছে}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"এখন শুরু করুন"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"হেড ট্র্যাকিং"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"রিঙ্গার মোড পরিবর্তন করতে ট্যাপ করুন"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"রিঙ্গার মোড"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"মিউট করুন"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"আনমিউট করুন"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ভাইব্রেট করান"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"স্প্লিট স্ক্রিন ব্যবহার করার সময় বাঁদিকের বা উপরের অ্যাপে পাল্টে নিন"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"\'স্প্লিট স্ক্রিন\' থাকাকালীন: একটি অ্যাপ থেকে অন্যটিতে পাল্টান"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"ডিসপ্লের মধ্যে একটি থেকে অপরটিতে অ্যাক্টিভ উইন্ডোটি সরান"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"উইন্ডো বাঁদিকে সরান"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"উইন্ডো ডানদিকে সরান"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"উইন্ডো বড় করুন"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"উইন্ডো ছোট করুন"</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>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"সিস্টেম কন্ট্রোল"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"সিস্টেম অ্যাপ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"মাল্টিটাস্কিং"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"সম্প্রতি ব্যবহার করা অ্যাপ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"স্প্লিট স্ক্রিন"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ইনপুট"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"অ্যাপ শর্টকাট"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"কীবোর্ড শর্টকাট"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"কীবোর্ড শর্টকাট কাস্টমাইজ করুন"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"শর্টকাট সরাবেন?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"শর্টকাট অ্যাসাইন করতে কী প্রেস করুন"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"এটি আপনার কাস্টম শর্টকাট স্থায়ীভাবে মুছে ফেলবে।"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"শর্টকাট সার্চ করুন"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"কোনও সার্চ ফলাফল নেই"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"আইকন আড়াল করুন"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"অ্যাকশন বা মেটা কী আইকন"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"প্লাস আইকন"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"কাস্টমাইজ করুন"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"হয়ে গেছে"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"আইকন বড় করুন"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"যোগ চিহ্ন"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ফরওয়ার্ড স্ল্যাশ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"টেনে আনার হ্যান্ডেল"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"কীবোর্ড সেটিংস"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"শর্টকাট সেট করুন"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"সরান"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"বাতিল করুন"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"কী প্রেস করুন"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"কী কম্বিনেশন আগে থেকে ব্যবহার হচ্ছে। অন্য কী ব্যবহার করে দেখুন।"</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"আপনার কীবোর্ডে অ্যাকশন কী প্রেস করুন"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"দারুণ!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"আপনি \'সব অ্যাপের জেসচার দেখুন\' টিউটোরিয়াল সম্পূর্ণ করেছেন"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index ab5ffae..fe38ba8 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da otvorite aplikaciju pomoću vidžeta, morat ćete potvrditi identitet. Također imajte na umu da ih svako može pregledati, čak i ako je tablet zaključan. Neki vidžeti možda nisu namijenjeni za vaš zaključani ekran i njihovo dodavanje ovdje možda nije sigurno."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Razumijem"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Vidžeti"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Da dodate vidžete na zaključani ekran kao prečicu, provjerite je li ovo omogućeno u postavkama."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Da biste dodali prečac Widgeti, provjerite je li u postavkama omogućena opcija Prikaži widgete na zaključanom zaslonu."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Postavke"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zamijeni korisnika"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući meni"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci iz ove sesije će se izbrisati."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Obavještenja"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Razgovori"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Obriši sva nečujna obavještenja"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Obavještenja su pauzirana načinom rada Ne ometaj"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nema obavještenja}=1{Obavještenja su pauzirana putem načina rada {mode}}=2{Obavještenja su pauzirana putem načina rada {mode} i još jednog načina rada}one{Obavještenja su pauzirana putem načina rada {mode} i još # načina rada}few{Obavještenja su pauzirana putem načina rada {mode} i još # načina rada}other{Obavještenja su pauzirana putem načina rada {mode} i još # načina rada}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Započni odmah"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Praćenje položaja glave"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Dodirnite da promijenite način rada zvuka zvona"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"način rada za zvuk zvona"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključite zvuk"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključite zvuk"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Pređite u aplikaciju lijevo ili iznad dok koristite podijeljeni ekran"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Za vrijeme podijeljenog ekrana: zamjena jedne aplikacije drugom"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Premještanje aktivnog prozora između ekrana"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Pomicanje prozora ulijevo"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Pomicanje prozora udesno"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maksimiziranje prozora"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimiziranje prozora"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Unos"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Prebacivanje na sljedeći jezik"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Prebacivanje na prethodni jezik"</string>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Sistemske kontrole"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistemske aplikacije"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Nedavne aplikacije"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Podijeljeni ekran"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Unos"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Prečice aplikacije"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Prečice tastature"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prilagodite prečice na tastaturi"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Ukloniti prečicu?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Želite li vratiti na zadano?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Pritisnite tipku da dodijelite prečicu"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ovo će trajno izbrisati prilagođenu prečicu."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Time će se trajno izbrisati svi vaši prilagođeni prečaci."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečica pretraživanja"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nema rezultata pretraživanja"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sužavanja"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona tipke radnji ili meta tipka"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona znaka plus"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagođavanje"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotovo"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona proširivanja"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"kosa crta"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ručica za prevlačenje"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Postavke tastature"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Postavi prečicu"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Ukloni"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Da, vrati na zadano"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Otkaži"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pritisnite tipku"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Ta se kombinacija tipki već koristi. Pokušajte s drugom tipkom."</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite tipku radnji na tastaturi"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Odlično!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Izvršili ste pokret za prikaz svih aplikacija"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tastature"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 152e1ad..ba3c4ad 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Per obrir una aplicació utilitzant un widget, necessitaràs verificar la teva identitat. També has de tenir en compte que qualsevol persona pot veure els widgets, fins i tot quan la tauleta està bloquejada. És possible que alguns widgets no estiguin pensats per a la pantalla de bloqueig i que no sigui segur afegir-los-hi."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entesos"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Per afegir widgets a la pantalla de bloqueig com a drecera, assegura\'t que estiguin activats a la configuració."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Per afegir la drecera Widgets, assegura\'t que l\'opció Mostra els widgets a la pantalla de bloqueig estigui activada a la configuració."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Configuració"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Canvia d\'usuari"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Totes les aplicacions i les dades d\'aquesta sessió se suprimiran."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificacions"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Converses"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Esborra totes les notificacions silencioses"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificacions pausades pel mode No molestis"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{No hi ha cap notificació}=1{{mode} ha posat en pausa les notificacions}=2{{mode} i un altre mode han posat en pausa les notificacions}many{{mode} i # de modes més han posat en pausa les notificacions}other{{mode} i # modes més han posat en pausa les notificacions}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Comença ara"</string>
@@ -704,6 +707,8 @@
<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>
<string name="volume_ringer_mode" msgid="6867838048430807128">"mode de timbre"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"deixar de silenciar"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Canvia a l\'aplicació de l\'esquerra o de dalt amb la pantalla dividida"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Durant el mode de pantalla dividida: substitueix una app per una altra"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Mou la finestra activa entre pantalles"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Mou la finestra cap a l\'esquerra"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Mou la finestra cap a la dreta"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximitza la finestra"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimitza la finestra"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Canvia a l\'idioma següent"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Caniva a l\'idioma anterior"</string>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Controls del sistema"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplicacions del sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasca"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Aplicacions recents"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantalla dividida"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Dreceres d\'aplicacions"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tecles de drecera"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalitza les tecles de drecera"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Vols suprimir la drecera?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vols restablir els valors predeterminats?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Prem la tecla per assignar la drecera"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Aquesta acció suprimirà la drecera personalitzada permanentment."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Aquesta acció suprimirà totes les dreceres personalitzades permanentment."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Dreceres de cerca"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No hi ha cap resultat de la cerca"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Replega la icona"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icona de la tecla d\'acció o Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icona del signe més"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalitza"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Fet"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Desplega la icona"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"més"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"barra inclinada"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ansa per arrossegar"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configuració del teclat"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Configura la drecera"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Suprimeix"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sí, restableix"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel·la"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Prem una tecla"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"La combinació de tecles ja s\'està utilitzant. Prova-ho amb una altra tecla."</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Prem la tecla d\'acció al teclat"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Enhorabona!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Has completat el gest per veure totes les aplicacions"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index ac0c6d6..317061f 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"K otevření aplikace pomocí widgetu budete muset ověřit svou totožnost. Také mějte na paměti, že widgety uvidí kdokoli, i když tablet bude uzamčen. Některé widgety nemusí být pro obrazovku uzamčení určeny a nemusí být bezpečné je na ni přidat."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Rozumím"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgety"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Pokud chcete přidat Widgety na obrazovku uzamčení jako zkratku, musí to být povoleno v nastavení."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Přepnout uživatele"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rozbalovací nabídka"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Veškeré aplikace a data v této relaci budou vymazána."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Oznámení"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Konverzace"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Vymazat všechna tichá oznámení"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Oznámení jsou pozastavena režimem Nerušit"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Žádná oznámení}=1{Oznámení jsou pozastavená režimem {mode}}=2{Oznámení jsou pozastavená režimem {mode} a 1 dalším}few{Oznámení jsou pozastavená režimem {mode} a # dalšími}many{Oznámení jsou pozastavená režimem {mode} a # dalšího}other{Oznámení jsou pozastavená režimem {mode} a # dalšími}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Spustit"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Sledování hlavy"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Klepnutím změníte režim vyzvánění"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"režim vyzvánění"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vypnout zvuk"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"zapnout zvuk"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrovat"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Přepnout na aplikaci vlevo nebo nahoře v režimu rozdělené obrazovky"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"V režimu rozdělené obrazovky: nahradit jednu aplikaci druhou"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Přesunout aktivní okno mezi obrazovkami"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Přesunout okno doleva"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Přesunout okno doprava"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximalizovat okno"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimalizovat okno"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Vstup"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Přepnout na další jazyk"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Přepnout na předchozí jazyk"</string>
@@ -1391,7 +1394,7 @@
<string name="assistant_attention_content_description" msgid="4166330881435263596">"Je zjištěna přítomnost uživatele"</string>
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Výchozí aplikaci pro poznámky nastavíte v Nastavení"</string>
<string name="install_app" msgid="5066668100199613936">"Nainstalovat aplikaci"</string>
- <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"Pokračujte přejetím"</string>
+ <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"Pokračovat přejetím nahoru"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Zrcadlit na externí displej?"</string>
<string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Vnitřní displej bude zrcadlen. Přední displej bude vypnutý."</string>
<string name="mirror_display" msgid="2515262008898122928">"Zrcadlit displej"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Ovládací prvky systému"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systémové aplikace"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Poslední aplikace"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Rozdělená obrazovka"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Vstup"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Zkratky aplikací"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové zkratky"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Přizpůsobení klávesových zkratek"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Odstrabit zkratku?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Nastavte zkratku stisknutím klávesy"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Vlastní zkratka se trvale smaže."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhledat zkratky"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Žádné výsledky hledání"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sbalení"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona klávesy Akce nebo Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona Plus"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Přizpůsobit"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Hotovo"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalení"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"nebo"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"lomítko"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Úchyt pro přetažení"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Nastavení klávesnice"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Nastavit zkratku"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Odstranit"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Zrušit"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Stiskněte klávesu"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinace kláves se už používá. Použijte jinou klávesu."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Stiskněte akční klávesu na klávesnici"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Výborně!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Provedli jste gesto k zobrazení všech aplikací"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvícení klávesnice"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Úroveň %1$d z %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládání domácnosti"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 968d44c..3c0dc42 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Hvis du vil åbne en app ved hjælp af en widget, skal du verificere din identitet. Husk også, at alle kan se dem, også når din tablet er låst. Nogle widgets er muligvis ikke beregnet til låseskærmen, og det kan være usikkert at tilføje dem her."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Hvis du vil tilføje widgets på låseskærmen som en genvej, skal du sørge for, at funktionen er aktiveret i indstillingerne."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skift bruger"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullemenu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps og data i denne session slettes."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifikationer"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Samtaler"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Ryd alle lydløse notifikationer"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifikationer er sat på pause af Forstyr ikke"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Ingen notifikationer}=1{Notifikationer er sat på pause af {mode}}=2{Notifikationer er sat på pause af {mode} og én anden tilstand}one{Notifikationer er sat på pause af {mode} og # anden tilstand}other{Notifikationer er sat på pause af {mode} og # andre tilstande}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Start nu"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Hovedregistrering"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tryk for at ændre ringetilstand"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ringetilstand"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"slå lyden fra"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå lyden til"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrer"</string>
@@ -875,14 +882,10 @@
<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>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Ved opdelt skærm: Udskift én app med en anden"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Flyt det aktive vindue fra skærm til skærm"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Flyt vinduet til venstre"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Flyt vinduet til højre"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maksimér vinduet"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimer vinduet"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Skift til næste sprog"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Skift til forrige sprog"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Systemstyring"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systemapps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Seneste apps"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Opdelt skærm"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Appgenveje"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tastaturgenveje"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tilpas tastaturgenveje"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Skal genvejen fjernes?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Tryk på en tast for at tildele genvej"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Denne handling sletter din tilpassede genvej permanent."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Genveje til søgning"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Der er ingen søgeresultater"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon for Skjul"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikon for handlingstast eller metatast"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plusikon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Tilpas"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Udfør"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon for Udvid"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"skråstreg"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Håndtag"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastaturindstillinger"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Konfigurer genvej"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Fjern"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annuller"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tryk på en tast"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tastekombinationen er allerede i brug. Prøv en anden tast."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tryk på handlingstasten på dit tastatur"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Flot klaret!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Du har udført bevægelsen for at se alle apps"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturets baggrundslys"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d af %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Hjemmestyring"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 4994130..8d13524 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Wenn du eine App mit einem Widget öffnen möchtest, musst du deine Identität bestätigen. Beachte auch, dass jeder die Widgets sehen kann, auch wenn dein Tablet gesperrt ist. Einige Widgets sind möglicherweise nicht für den Sperrbildschirm vorgesehen, sodass es unsicher sein kann, sie hier hinzuzufügen."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ok"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Wenn du die Funktion „Widgets auf dem Sperrbildschirm“ als Verknüpfung hinzufügen möchtest, musst du diese Funktion zuerst in den Einstellungen aktivieren."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Nutzer wechseln"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Pull-down-Menü"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle Apps und Daten in dieser Sitzung werden gelöscht."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Benachrichtigungen"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Unterhaltungen"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Alle lautlosen Benachrichtigungen löschen"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Benachrichtigungen durch „Bitte nicht stören“ pausiert"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Keine Benachrichtigungen}=1{Benachrichtigungen durch {mode} pausiert}=2{Benachrichtigungen durch {mode} und einen weiteren Modus pausiert}other{Benachrichtigungen durch {mode} und # weitere Modi pausiert}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Jetzt starten"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Erfassung von Kopfbewegungen"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Zum Ändern des Klingeltonmodus tippen"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"Klingeltonmodus"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"Stummschalten"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"Aufheben der Stummschaltung"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"Vibrieren lassen"</string>
@@ -875,14 +882,10 @@
<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="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Aktives Fenster auf anderes Display verschieben"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Fenster nach links verschieben"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Fenster nach rechts verschieben"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Fenster maximieren"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Fenster minimieren"</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">"Zur vorherigen Sprache wechseln"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Systemsteuerelemente"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System-Apps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Zuletzt verwendete Apps"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Splitscreen"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Eingabe"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App-Verknüpfungen"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tastenkürzel"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tastenkombinationen anpassen"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Tastenkombination entfernen?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Drücke eine Taste, um eine Tastenkombination festzulegen"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Dadurch wird die benutzerdefinierte Tastenkombination endgültig gelöscht."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tastenkürzel suchen"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Keine Suchergebnisse"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Symbol „Minimieren“"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Symbol für Aktions- oder Meta-Taste"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plussymbol"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Anpassen"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Fertig"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Symbol „Maximieren“"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oder"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"Schrägstrich"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ziehpunkt"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastatureinstellungen"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Tastenkombination festlegen"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Entfernen"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Abbrechen"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Taste drücken"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Diese Tastenkombination wird bereits verwendet. Versuche es mit einer anderen Taste."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Drücke die Aktionstaste auf deiner Tastatur"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Perfekt!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Du hast das Tutorial für die Touch-Geste zum Aufrufen aller Apps abgeschlossen"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturbeleuchtung"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d von %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Smart-Home-Steuerung"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 1e4ef85..b6de539 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Για να ανοίξετε μια εφαρμογή χρησιμοποιώντας ένα γραφικό στοιχείο, θα πρέπει να επαληθεύσετε την ταυτότητά σας. Επίσης, λάβετε υπόψη ότι η προβολή τους είναι δυνατή από οποιονδήποτε, ακόμα και όταν το tablet σας είναι κλειδωμένο. Ορισμένα γραφικά στοιχεία μπορεί να μην προορίζονται για την οθόνη κλειδώματος και η προσθήκη τους εδώ ενδέχεται να μην είναι ασφαλής."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Το κατάλαβα"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Για να προσθέσετε το Widgets στην οθόνη κλειδώματος ως συντόμευση, βεβαιωθείτε ότι είναι ενεργοποιημένο στις ρυθμίσεις."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Εναλλαγή χρήστη"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"αναπτυσσόμενο μενού"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Όλες οι εφαρμογές και τα δεδομένα αυτής της περιόδου σύνδεσης θα διαγραφούν."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Ειδοποιήσεις"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Συζητήσεις"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Διαγραφή όλων των ειδοποιήσεων σε σίγαση"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Οι ειδοποιήσεις τέθηκαν σε παύση από τη λειτουργία \"Μην ενοχλείτε\""</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Δεν υπάρχουν ειδοποιήσεις}=1{Οι ειδοποιήσεις τέθηκαν σε παύση από τη λειτουργία {mode}}=2{Οι ειδοποιήσεις τέθηκαν σε παύση από τη λειτουργία {mode} και μία άλλη λειτουργία}other{Οι ειδοποιήσεις τέθηκαν σε παύση από τη λειτουργία {mode} και # άλλες λειτουργίες}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Έναρξη τώρα"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Παρακ. κίνησ. κεφαλής"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Πατήστε για να αλλάξετε τη λειτουργία ειδοποίησης ήχου"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"λειτουργία ειδοποίησης ήχου"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"σίγαση"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"κατάργηση σίγασης"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"δόνηση"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Εναλλαγή σε εφαρμογή αριστερά ή επάνω κατά τη χρήση διαχωρισμού οθόνης"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Κατά τον διαχωρισμό οθόνης: αντικατάσταση μιας εφαρμογής με άλλη"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Μετακίνηση ενεργού παραθύρου μεταξύ οθονών"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Μετακίνηση παραθύρου αριστερά"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Μετακίνηση παραθύρου δεξιά"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Μεγιστοποίηση παραθύρου"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Ελαχιστοποίηση παραθύρου"</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>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Στοιχεία ελέγχου συστήματος"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Εφαρμογές συστήματος"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Πολυδιεργασία"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Πρόσφατες εφαρμογές"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Διαχωρισμός οθόνης"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Εισαγωγή"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Συντομεύσεις εφαρμογών"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Συντομεύσεις πληκτρολογίου"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Προσαρμογή συντομεύσεων πληκτρολογίου"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Κατάργηση συντόμευσης;"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Πατήστε το πλήκτρο για ανάθεση της συντόμευσης"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Με αυτή την ενέργεια, η προσαρμοσμένη συντόμευση θα διαγραφεί οριστικά."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Συντομεύσεις αναζήτησης"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Κανένα αποτέλεσμα αναζήτησης"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Εικονίδιο σύμπτυξης"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Εικονίδιο πλήκτρου ενέργειας ή Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Εικονίδιο συν"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Προσαρμογή"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Τέλος"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Εικονίδιο ανάπτυξης"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ή"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"συν"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"κάθετος"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Λαβή μεταφοράς"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ρυθμίσεις πληκτρολογίου"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ορισμός συντόμευσης"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Κατάργηση"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Ακύρωση"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Πατήστε ένα πλήκτρο"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Ο συνδυασμός πλήκτρων χρησιμοποιείται ήδη. Δοκιμάστε άλλο πλήκτρο."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Πατήστε το πλήκτρο ενέργειας στο πληκτρολόγιό σας"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Μπράβο!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Ολοκληρώσατε την κίνηση για την προβολή όλων των εφαρμογών"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 938caa1..609e7af 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"To add widgets on the lock screen as a shortcut, make sure that it is enabled in settings."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Clear all silent notifications"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifications paused by Do Not Disturb"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{No notifications}=1{Notifications paused by {mode}}=2{Notifications paused by {mode} and one other mode}other{Notifications paused by {mode} and # other modes}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Head tracking"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to the app on the left or above while using split screen"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"During split screen: Replace an app from one to another"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Move active window between displays"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Move window to the left"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Move window to the right"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximise window"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimise window"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Switch to next language"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Switch to previous language"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"System controls"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System apps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Recent apps"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Customise keyboard shortcuts"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remove shortcut?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Press key to assign shortcut"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"This will delete your custom shortcut permanently."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Action or Meta key icon"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plus icon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Customise"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Done"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"forward slash"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard settings"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Set shortcut"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Remove"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Press key"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Key combination already in use. Try another key."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Well done!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"You completed the view all apps gesture"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index dada5e9..be4e81f 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you’ll need to verify it’s you. Also, keep in mind that anyone can view them, even when your tablet’s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"To add Widgets on the lock screen as a shortcut, make sure it is enabled in settings."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"To add the \"Widgets\" shortcut, make sure \"Show widgets on lock screen\" is enabled in settings."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Settings"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -590,6 +591,7 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Clear all silent notifications"</string>
+ <string name="accessibility_notification_section_header_open_settings" msgid="6235202417954844004">"Open notifications settings"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifications paused by Do Not Disturb"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{No notifications}=1{Notifications paused by {mode}}=2{Notifications paused by {mode} and one other mode}other{Notifications paused by {mode} and # other modes}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
@@ -704,6 +706,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Head Tracking"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, tap to change ringer mode"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
@@ -1415,7 +1418,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"System controls"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System apps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Recent apps"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
@@ -1424,14 +1426,17 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Customize keyboard shortcuts"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remove shortcut?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Reset back to default?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Press key to assign shortcut"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"This will delete your custom shortcut permanently."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"This will delete all your custom shortcuts permanently."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Action or Meta key icon"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plus icon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Customize"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Reset"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Done"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
@@ -1441,6 +1446,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard Settings"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Set shortcut"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Remove"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Yes, reset"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Press key"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Key combination already in use. Try another key."</string>
@@ -1472,6 +1478,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Well done!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"You completed the view all apps gesture"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Tutorial animation, click to pause and resume play."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Home Controls"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 938caa1..609e7af 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"To add widgets on the lock screen as a shortcut, make sure that it is enabled in settings."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Clear all silent notifications"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifications paused by Do Not Disturb"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{No notifications}=1{Notifications paused by {mode}}=2{Notifications paused by {mode} and one other mode}other{Notifications paused by {mode} and # other modes}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Head tracking"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to the app on the left or above while using split screen"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"During split screen: Replace an app from one to another"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Move active window between displays"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Move window to the left"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Move window to the right"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximise window"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimise window"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Switch to next language"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Switch to previous language"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"System controls"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System apps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Recent apps"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Customise keyboard shortcuts"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remove shortcut?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Press key to assign shortcut"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"This will delete your custom shortcut permanently."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Action or Meta key icon"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plus icon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Customise"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Done"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"forward slash"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard settings"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Set shortcut"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Remove"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Press key"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Key combination already in use. Try another key."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Well done!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"You completed the view all apps gesture"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 938caa1..609e7af 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"To add widgets on the lock screen as a shortcut, make sure that it is enabled in settings."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Clear all silent notifications"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifications paused by Do Not Disturb"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{No notifications}=1{Notifications paused by {mode}}=2{Notifications paused by {mode} and one other mode}other{Notifications paused by {mode} and # other modes}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Head tracking"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to the app on the left or above while using split screen"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"During split screen: Replace an app from one to another"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Move active window between displays"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Move window to the left"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Move window to the right"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximise window"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimise window"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Switch to next language"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Switch to previous language"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"System controls"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System apps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Recent apps"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Customise keyboard shortcuts"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remove shortcut?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Press key to assign shortcut"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"This will delete your custom shortcut permanently."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Action or Meta key icon"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plus icon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Customise"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Done"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"forward slash"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard settings"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Set shortcut"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Remove"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Press key"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Key combination already in use. Try another key."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Well done!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"You completed the view all apps gesture"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 5389aba..177e690 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -450,7 +450,7 @@
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"Listo"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configuración"</string>
<string name="zen_mode_on" msgid="9085304934016242591">"Activado"</string>
- <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Sí • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Activado • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="zen_mode_off" msgid="1736604456618147306">"Desactivado"</string>
<string name="zen_mode_set_up" msgid="8231201163894922821">"Sin establecer"</string>
<string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Administrar en configuración"</string>
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir una app usando un widget, debes verificar tu identidad. Además, ten en cuenta que cualquier persona podrá verlo, incluso cuando la tablet esté bloqueada. Es posible que algunos widgets no se hayan diseñados para la pantalla de bloqueo y podría ser peligroso agregarlos allí."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Para agregar Widgets en la pantalla de bloqueo como combinación de teclas, asegúrate de que la función esté habilitada en la configuración."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú expandible"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán las aplicaciones y los datos de esta sesión."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificaciones"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversaciones"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Borrar todas las notificaciones silenciosas"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificaciones pausadas por el modo \"No interrumpir\""</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{No hay notificaciones}=1{{mode} pausó las notificaciones}=2{{mode} y un modo más pausaron las notificaciones}many{{mode} y # de modos más pausaron las notificaciones}other{{mode} y # modos más pausaron las notificaciones}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Comenzar ahora"</string>
@@ -704,6 +709,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Monitoreo de cabeza"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Presiona para cambiar el modo de timbre"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"modo de timbre"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, presiona para cambiar el modo de timbre"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"dejar de silenciar"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -875,14 +881,10 @@
<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="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Mover la ventana activa de una pantalla a otra"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Mover la ventana hacia la izquierda"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Mover la ventana hacia la derecha"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximizar ventana"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimizar ventana"</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>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Cambiar al idioma anterior"</string>
@@ -1419,7 +1421,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Controles del sistema"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Apps del sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Tareas múltiples"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Apps recientes"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantalla dividida"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Accesos directos a aplicaciones"</string>
@@ -1428,25 +1429,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personaliza las combinaciones de teclas"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"¿Quieres quitar el acceso directo?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Presiona la tecla para asignar el acceso directo"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Esta acción borrará tu acceso directo personalizado de forma permanente."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Buscar combinaciones de teclas"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"La búsqueda no arrojó resultados"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícono de contraer"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ícono tecla meta o de acción"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ícono de signo más"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Listo"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícono de expandir"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"más"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"barra diagonal"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configuración del teclado"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Establecer combinación de teclas"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Quitar"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Presiona una tecla"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"La combinación de teclas ya está en uso. Prueba con otra."</string>
@@ -1478,6 +1485,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Presiona la tecla de acción en el teclado"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"¡Bien hecho!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Completaste el gesto para ver todas las apps"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animación del instructivo. Haz clic para pausar y reanudar la reproducción."</string>
<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>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 2ff38b0..53289d1 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -306,7 +306,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Compartir audio"</string>
- <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="8680997711431098238">"Permite compartir audio"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="8680997711431098238">"Admite Compartir audio"</string>
<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>
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir una aplicación usando un widget, deberás verificar que eres tú. Además, ten en cuenta que cualquier persona podrá verlos, incluso aunque tu tablet esté bloqueada. Es posible que algunos widgets no estén pensados para la pantalla de bloqueo y no sea seguro añadirlos aquí."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Para añadir la función de widgets en la pantalla de bloqueo como combinación de teclas, asegúrate de que la opción esté habilitada en los ajustes."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para añadir el acceso directo Widgets, asegúrate de que la opción Mostrar widgets en la pantalla de bloqueo esté habilitada en los ajustes."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Ajustes"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar de usuario"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán todas las aplicaciones y datos de esta sesión."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificaciones"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversaciones"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Borrar todas las notificaciones silenciosas"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificaciones pausadas por el modo No molestar"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{No hay notificaciones}=1{Notificaciones pausadas por {mode}}=2{Notificaciones pausadas por {mode} y un modo más}many{Notificaciones pausadas por {mode} y # modos más}other{Notificaciones pausadas por {mode} y # modos más}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Empezar ahora"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seguimiento de cabeza"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toca para cambiar el modo de timbre"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"modo de timbre"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"dejar de silenciar"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Cambiar a la app de la izquierda o de arriba en pantalla dividida"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Con pantalla dividida: reemplazar una aplicación por otra"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Mover ventana activa de una pantalla a otra"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Mover ventana a la izquierda"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Mover ventana a la derecha"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximizar ventana"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimizar ventana"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Cambiar a siguiente idioma"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Cambiar a idioma anterior"</string>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Controles del sistema"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplicaciones del sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarea"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Aplicaciones recientes"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantalla dividida"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Accesos directos a aplicaciones"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizar las combinaciones de teclas"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"¿Eliminar combinación de teclas?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"¿Restablecer valores predeterminados?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Pulsa una tecla para asignar una combinación de teclas"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Se eliminará tu combinación de teclas personalizada de forma permanente."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Se eliminarán todos tus accesos directos personalizados de forma permanente."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atajos de búsqueda"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No hay resultados de búsqueda"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icono de contraer"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icono de la tecla de acción o de la tecla Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icono de más"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Hecho"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icono de desplegar"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"más"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"barra inclinada"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ajustes del teclado"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Establecer combinación de teclas"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Eliminar"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sí, restablecer"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pulsa una tecla"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"La combinación de teclas ya se está usando. Prueba con otra tecla."</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pulsa la tecla de acción de tu teclado"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"¡Muy bien!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Has completado el gesto para ver todas las aplicaciones"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index f646e25..165f9c2 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Rakenduse avamiseks vidina abil peate kinnitama, et see olete teie. Samuti pidage meeles, et kõik saavad vidinaid vaadata, isegi kui teie tahvelarvuti on lukus. Mõni vidin ei pruugi olla ette nähtud teie lukustuskuva jaoks ja seda pole turvaline siia lisada."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Selge"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Vidinad"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Selleks et lisada vidinad otseteena lukustuskuvale, veenduge, et see oleks seadetes lubatud."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kasutaja vahetamine"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rippmenüü"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Seansi kõik rakendused ja andmed kustutatakse."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Märguanded"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Vestlused"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Kustuta kõik hääletud märguanded"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Režiim Mitte segada peatas märguanded"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Märguandeid pole}=1{{mode} peatas märguanded}=2{{mode} ja veel üks režiim peatasid märguanded}other{{mode} ja veel # režiimi peatasid märguanded}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Alusta kohe"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Pea jälgimine"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Puudutage telefonihelina režiimi muutmiseks"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"telefonihelina režiim"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vaigistamine"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"vaigistuse tühistamine"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibreerimine"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Vasakule või ülemisele rakendusele lülitamine jagatud ekraani ajal"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Ekraanikuva jagamise ajal: ühe rakenduse asendamine teisega"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Aktiivse akna teisaldamine ekraanide vahel"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Akna vasakule liigutamine"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Akna paremale liigutamine"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Akna maksimeerimine"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Akna minimeerimine"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Sisend"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Järgmisele keelele lülitamine"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Eelmisele keelele lülitamine"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Süsteemi juhtelemendid"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Süsteemirakendused"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitegumtöö"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Hiljutised rakendused"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Jagatud ekraanikuva"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Sisend"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Rakenduse otseteed"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatuuri otseteed"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Klaviatuuri otseteede kohandamine"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Kas soovite otsetee eemaldada?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Otsetee lisamiseks vajutage klahvi"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"See kustutab teie kohandatud otsetee jäädavalt."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Otsige otseteid"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Otsingutulemused puuduvad"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ahendamisikoon"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Toiming või metaklahv"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Pluss-ikoon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Kohandamine"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Valmis"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laiendamisikoon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"või"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"pluss"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"kaldkriips"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Lohistamispide"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatuuri seaded"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Määrake otsetee"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Eemalda"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Tühista"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Vajutage klahvi"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Klahvikombinatsioon on juba kasutusel. Proovige mõnda muud klahvi."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Vajutage klaviatuuril toiminguklahvi"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Hästi tehtud!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Tegite kõigi rakenduste vaatamise liigutuse"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatuuri taustavalgustus"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Tase %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kodu juhtelemendid"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 7d3c733..46bbdc3 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Aplikazio bat widget baten bidez irekitzeko, zeu zarela egiaztatu beharko duzu. Gainera, kontuan izan edonork ikusi ahalko dituela halako widgetak, tableta blokeatuta badago ere. Baliteke widget batzuk pantaila blokeaturako egokiak ez izatea, eta agian ez da segurua haiek bertan gehitzea."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ados"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgetak"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Pantaila blokeatuan lasterbide gisa widgetak gehitzeko, ziurtatu aukera hori gaituta dagoela ezarpenetan."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Aldatu erabiltzailea"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"zabaldu menua"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Saioko aplikazio eta datu guztiak ezabatuko dira."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Jakinarazpenak"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Elkarrizketak"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Garbitu soinurik gabeko jakinarazpen guztiak"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Ez molestatzeko moduak pausatu egin ditu jakinarazpenak"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Ez dago jakinarazpenik}=1{Modu honek jakinarazpenak pausatu ditu: {mode}}=2{Modu honek eta beste modu batek jakinarazpenak pausatu dituzte: {mode}}other{Modu honek eta beste # moduk jakinarazpenak pausatu dituzte: {mode}}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Hasi"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Buruaren jarraipena"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Sakatu tonu-jotzailearen modua aldatzeko"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"tonu-jotzailearen modua"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desaktibatu audioa"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktibatu audioa"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"dardara"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Aldatu ezkerreko edo goiko aplikaziora pantaila zatitua erabiltzean"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Pantaila zatituan zaudela, ordeztu aplikazio bat beste batekin"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Eraman leiho aktiboa pantaila batetik bestera"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Eraman leihoa ezkerrera"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Eraman leihoa eskuinera"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximizatu leihoa"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimizatu leihoa"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Sarrera"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Aldatu hurrengo hizkuntzara"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Aldatu aurreko hizkuntzara"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Sistema kontrolatzeko aukerak"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistemaren aplikazioak"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Zeregin bat baino gehiago aldi berean exekutatzea"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Azkenaldiko aplikazioak"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantaila zatitzea"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Sarrera"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Aplikazioetarako lasterbideak"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Lasterbideak"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Pertsonalizatu lasterbideak"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Lasterbidea kendu nahi duzu?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Sakatu tekla lasterbidea esleitzeko"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Betiko ezabatuko da lasterbide pertsonalizatua."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bilatu lasterbideak"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ez dago bilaketa-emaitzarik"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tolesteko ikonoa"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ekintzaren edo Meta teklaren ikonoa"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plus-ikonoa"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Pertsonalizatu"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Eginda"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Zabaltzeko ikonoa"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"edo"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"gehi"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"barra"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Arrastatzeko kontrol-puntua"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Teklatuaren ezarpenak"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ezarri lasterbidea"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Kendu"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Utzi"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Sakatu tekla"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tekla-konbinazio hori erabili da dagoeneko. Probatu beste tekla bat."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Sakatu teklatuko ekintza-tekla"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Bikain!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Osatu duzu aplikazio guztiak ikusteko keinua"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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 gailuen kontrola"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 153a460..5e68a95c 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"برای باز کردن برنامه بااستفاده از ابزاره، باید هویت خودتان را بهتأیید برسانید. همچنین، بهخاطر داشته باشید که همه میتوانند آنها را مشاهده کنند، حتی وقتی رایانه لوحیتان قفل است. برخیاز ابزارهها ممکن است برای صفحه قفل درنظر گرفته نشده باشند و ممکن است اضافه کردن آنها در اینجا ناامن باشد."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"متوجهام"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ابزارهها"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"برای افزودن «ابزارهها» در صفحه قفل بهعنوان میانبر، مطمئن شوید این گزینه در تنظیمات فعال باشد."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تغییر کاربر"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"منوی پایینپر"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"همه برنامهها و دادههای این جلسه حذف خواهد شد."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"اعلانها"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"مکالمهها"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"پاک کردن همه اعلانهای بیصدا"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"اعلانها توسط «مزاحم نشوید» موقتاً متوقف شدند"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{اعلانی موقتاً متوقف نشده است}=1{اعلانها را «{mode}» موقتاً متوقف کرده است}=2{اعلانها را «{mode}» و یک حالت دیگر موقتاً متوقف کرداند}one{اعلانها را «{mode}» و # حالت دیگر موقتاً متوقف کردهاند}other{اعلانها را «{mode}» و # حالت دیگر موقتاً متوقف کردهاند}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"اکنون شروع کنید"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ردیابی سر"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"برای تغییر حالت زنگ، تکضرب بزنید"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"حالت زنگ"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"صامت کردن"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"باصدا کردن"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"لرزش"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"رفتن به برنامه سمت چپ یا بالا درحین استفاده از صفحهٔ دونیمه"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"درحین صفحهٔ دونیمه: برنامهای را با دیگری جابهجا میکند"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"جابهجا کردن پنجره فعال بین نمایشگرها"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"بردن پنجره به چپ"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"بردن پنجره به راست"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"بهحداکثر رساندن اندازه پنجره"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"بهحداقل رساندن اندازه پنجره"</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>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"کنترلهای سیستم"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"برنامههای سیستم"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"چندوظیفگی"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"برنامههای اخیر"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"صفحهٔ دونیمه"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ورودی"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"میانبرهای برنامه"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"میانبرهای صفحهکلید"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"سفارشیسازی کردن میانبرهای صفحهکلید"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"میانبر حذف شود؟"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"برای اختصاص دادن میانبر، کلید را فشار دهید"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"با این کار، میانبر سفارشی شما برای همیشه حذف میشود."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"جستجوی میانبرها"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"نتیجهای برای جستجو پیدا نشد"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"نماد جمع کردن"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"نماد کلید کنش یا متا"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"نماد جمع"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"سفارشیسازی کردن"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"تمام"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"نماد ازهم بازکردن"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"و"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"کجخط روبه جلو"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"دستگیره کشاندن"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"تنظیمات صفحهکلید"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"تنظیم میانبر"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"حذف"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"لغو"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"کلید را فشار دهید"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"ترکیب کلید ازقبل درحال استفاده است. کلید دیگری را امتحان کنید."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"دکمه کنش را روی صفحه لمسی فشار دهید"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"عالی بود!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"اشاره «مشاهده همه برنامهها» را تمام کردید"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 76d66c5..94b6364 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Jos haluat avata sovelluksen käyttämällä widgetiä, sinun täytyy vahvistaa henkilöllisyytesi. Muista myös, että widgetit näkyvät kaikille, vaikka tabletti olisi lukittuna. Jotkin widgetit on ehkä tarkoitettu lukitusnäytölle, ja niiden lisääminen tänne ei välttämättä ole turvallista."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Selvä"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgetit"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Jos haluat lisätä Widgetit lukitusnäytöllä ‑ominaisuuden pikakuvakkeeksi, varmista, että se on otettu käyttöön asetuksissa."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Jos haluat lisätä Widgetit-pikakuvakkeen, varmista, että \"Näytä widgetit lukitusnäytöllä\" on käytössä asetuksissa."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Asetukset"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Vaihda käyttäjää"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"alasvetovalikko"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Kaikki sovellukset ja tämän istunnon tiedot poistetaan."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Ilmoitukset"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Keskustelut"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Tyhjennä kaikki hiljaiset ilmoitukset"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Älä häiritse ‑tila keskeytti ilmoitukset"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Ei ilmoituksia}=1{{mode} keskeytti ilmoitukset}=2{{mode} ja yksi muu tila keskeytti ilmoitukset}other{{mode} ja # muuta tilaa keskeytti ilmoitukset}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Aloita nyt"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Pään seuranta"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Vaihda soittoäänen tilaa napauttamalla"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"Soittoäänen tila"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mykistä"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"poista mykistys"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"värinä"</string>
@@ -875,14 +880,10 @@
<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="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Siirrä aktiivinen ikkuna näytöltä toiselle"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Siirrä ikkuna vasemmalle"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Siirrä ikkuna oikealle"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Suurenna ikkuna"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Pienennä ikkuna"</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>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Järjestelmän hallinta"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Järjestelmäsovellukset"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitaskaus"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Viimeisimmät sovellukset"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Jaettu näyttö"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Syöte"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Sovellusten pikakuvakkeet"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Pikanäppäimet"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Pikanäppäimien muokkaaminen"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Poistetaanko pikanäppäin?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Palautetaanko oletusasetukset?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Määritä pikanäppäin painamalla näppäintä"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Oma pikanäppäin poistetaan pysyvästi."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Kaikki omat pikanäppäimet poistetaan pysyvästi."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pikahaut"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ei hakutuloksia"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tiivistyskuvake"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Toiminto- tai Meta-näppäinkuvake"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Pluskuvake"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Muokkaa"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Valmis"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laajennuskuvake"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"tai"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"kauttaviiva"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Vetokahva"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Näppäimistön asetukset"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Valitse pikanäppäin"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Poista"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Kyllä, nollaa"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Peru"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Paina näppäintä"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Näppäinyhdistelmä on jo käytössä. Kokeile toista näppäintä."</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Paina näppäimistön toimintonäppäintä"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Hienoa!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Olet oppinut Näytä kaikki sovellukset ‑eleen."</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Näppämistön taustavalo"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Taso %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kodin ohjaus"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 6997b26..ea9ee0b 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pour ouvrir une appli à l\'aide d\'un widget, vous devrez confirmer votre identité. En outre, gardez à l\'esprit que tout le monde peut voir les widgets, même lorsque votre tablette est verrouillée. Certains widgets n\'ont peut-être pas été conçus pour votre écran de verrouillage, et il pourrait être dangereux de les ajouter ici."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Pour ajouter des widgets comme raccourci à l\'écran de verrouillage, assurez-vous que cette fonctionnalité soit activée dans les paramètres."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applis et les données de cette session seront supprimées."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Effacer toutes les notifications silencieuses"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Les notifications sont suspendues par le mode Ne pas déranger"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Aucune notification}=1{Notifications suspendues par {mode}}=2{Notifications suspendues par {mode} et un autre mode}one{Notifications suspendues par {mode} et # autre mode}many{Notifications suspendues par {mode} et # d\'autres modes}other{Notifications suspendues par {mode} et # autres modes}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Commencer"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Suivi de la tête"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Touchez pour modifier le mode de sonnerie"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"mode de sonnerie"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"désactiver le son"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"réactiver le son"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibration"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Passer à l\'appli à gauche ou au-dessus avec l\'Écran divisé"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"En mode d\'écran divisé : remplacer une appli par une autre"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Déplacer la fenêtre active d\'un écran à l\'autre"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Déplacer la fenêtre vers la gauche"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Déplacer la fenêtre vers la droite"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Agrandir la fenêtre"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Réduire la fenêtre"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrée"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Passer à la langue suivante"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Passer à la langue précédente"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Commandes système"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Applis système"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitâche"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Applis récentes"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Écran divisé"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrée"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Raccourcis des applis"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis-clavier"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personnaliser les raccourcis-clavier"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Supprimer le raccourci?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Appuyez sur la touche pour attribuer un raccourci"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Cela supprimera définitivement votre raccourci personnalisé."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Rechercher des raccourcis"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Aucun résultat de recherche"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icône de la touche Action ou Méta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icône Plus"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personnaliser"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Terminé"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"barre oblique"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Poignée de déplacement"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Paramètres du clavier"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Définir un raccourci"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Supprimer"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annuler"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Appuyez sur la touche"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"La combinaison de touches est déjà utilisée. Essayez une autre touche."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Appuyez sur la touche d\'action de votre clavier"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Félicitations!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Vous avez appris le geste pour afficher toutes les applis"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 3e7fc38..bda70d9 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pour ouvrir une appli à l\'aide d\'un widget, vous devez confirmer qu\'il s\'agit bien de vous. N\'oubliez pas non plus que tout le monde peut voir vos widgets, même lorsque votre tablette est verrouillée. Certains d\'entre eux n\'ont pas été conçus pour l\'écran de verrouillage et les ajouter à cet endroit peut s\'avérer dangereux."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Pour ajouter des widgets à l\'écran de verrouillage en tant que raccourcis, assurez-vous que cette option est activée dans les paramètres."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Effacer toutes les notifications silencieuses"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifications suspendues par le mode Ne pas déranger"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Aucune notification}=1{Notifications suspendues par le mode {mode}}=2{Notifications suspendues par le mode {mode} et un autre mode}one{Notifications suspendues par le mode {mode} et # autre mode}many{Notifications suspendues par le mode {mode} et # d\'autres modes}other{Notifications suspendues par le mode {mode} et # autres modes}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Commencer"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Suivi de la tête"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Appuyez pour changer le mode de la sonnerie"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"mode de sonnerie"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"couper le son"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"réactiver le son"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"activer le vibreur"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Passez à l\'appli à gauche ou au-dessus avec l\'écran partagé"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"En mode écran partagé : Remplacer une appli par une autre"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Déplacer la fenêtre active d\'un écran à l\'autre"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Déplacer la fenêtre vers la gauche"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Déplacer la fenêtre vers la droite"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Agrandir la fenêtre"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Réduire la fenêtre"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Saisie"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Passer à la langue suivante"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Revenir à la langue précédente"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Commandes système"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Applis système"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitâche"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Applis récentes"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Écran partagé"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrée"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Raccourcis d\'application"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis clavier"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personnaliser les raccourcis clavier"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Supprimer le raccourci ?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Appuyez sur une touche pour attribuer un raccourci"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Votre raccourci personnalisé sera définitivement supprimé."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Rechercher des raccourcis"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Aucun résultat de recherche"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icône de touche d\'action ou de méta-touche"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icône Plus"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personnaliser"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"OK"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"barre oblique"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Poignée de déplacement"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Paramètres du clavier"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Définir un raccourci"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Supprimer"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annuler"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Appuyez sur la touche"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Combinaison de touches déjà utilisée. Essayez une autre touche."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Appuyez sur la touche d\'action de votre clavier"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Bravo !"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Vous avez appris le geste pour afficher toutes les applis"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d sur %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Contrôle de la maison"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index d36a24d..53c4417 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir unha aplicación mediante un widget, tes que verificar a túa identidade. Ten en conta que pode velos calquera persoa, mesmo coa tableta bloqueada. Pode ser que algúns widgets non estean pensados para a túa pantalla de bloqueo, polo que talvez non sexa seguro engadilos aquí."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Para poderes engadir widgets á pantalla de bloqueo como atallos, debe estar activada na configuración a opción correspondente."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú despregable"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Eliminaranse todas as aplicacións e datos desta sesión."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificacións"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversas"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Borrar todas as notificacións silenciadas"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"O modo Non molestar puxo en pausa as notificacións"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Non hai ningunha notificación}=1{Notificacións postas en pausa polo modo {mode}}=2{Notificacións postas en pausa polo modo {mode} e un máis}other{Notificacións postas en pausa polo modo {mode} e # máis}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Iniciar agora"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seguimento da cabeza"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toca para cambiar o modo de timbre"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"modo de timbre"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"activar o son"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Cambiar á aplicación da esquerda ou de arriba coa pantalla dividida"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"En modo de pantalla dividida: Substituír unha aplicación por outra"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Mover ventá activa entre pantallas"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Mover ventá á esquerda"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Mover ventá á dereita"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximizar ventá"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimizar ventá"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Cambiar ao seguinte idioma"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Cambiar ao idioma anterior"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Controis do sistema"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplicacións do sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarefa"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Aplicacións recentes"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantalla dividida"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atallos de aplicacións"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Atallos de teclado"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizar os atallos de teclado"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Queres quitar o atallo?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Preme a tecla para asignar o atallo"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Eliminarase de forma permanente o teu atallo personalizado."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Busca atallos"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Non hai resultados de busca"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona de contraer"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icona da tecla Meta ou de acción"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icona do signo máis"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Feito"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona de despregar"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"máis"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"barra oblicua"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configuración do teclado"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Definir atallo"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Quitar"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Preme unha tecla"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Xa se está usando esta combinación de teclas. Proba con outra."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Preme a tecla de acción do teclado"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Ben feito!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Completaches o titorial do xesto de ver todas as aplicacións"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 1c52868..8ff5717 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"વિજેટનો ઉપયોગ કરીને ઍપ ખોલવા માટે, તમારે એ ચકાસણી કરવાની જરૂર રહેશે કે આ તમે જ છો. તે ઉપરાંત, ધ્યાનમાં રાખો કે તમારું ટૅબ્લેટ લૉક કરેલું હોય તો પણ કોઈપણ વ્યક્તિ તેમને જોઈ શકે છે. અમુક વિજેટ કદાચ તમારી લૉક સ્ક્રીન માટે બનાવવામાં આવ્યા ન હોઈ શકે છે અને તેમને અહીં ઉમેરવાનું અસલામત હોઈ શકે છે."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"સમજાઈ ગયું"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"વિજેટ"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"લૉક સ્ક્રીન પર શૉર્ટકટ તરીકે વિજેટ ઉમેરવા માટે, ખાતરી કરો કે તે સેટિંગમાં ચાલુ કરેલા હોય."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"વિજેટ\"નો શૉર્ટકટ ઉમેરવા માટે, ખાતરી કરો કે સેટિંગમાં \"લૉક સ્ક્રીન પર વિજેટ બતાવો\" સુવિધા ચાલુ કરેલી છે."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"સેટિંગ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"વપરાશકર્તા સ્વિચ કરો"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"પુલડાઉન મેનૂ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"આ સત્રમાંની તમામ ઍપ અને ડેટા કાઢી નાખવામાં આવશે."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"નોટિફિકેશન"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"વાતચીત"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"બધા સાઇલન્ટ નોટિફિકેશન સાફ કરો"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"ખલેલ પાડશો નહીં દ્વારા થોભાવેલ નોટિફિકેશન"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{કોઈ નોટિફિકેશન નથી}=1{{mode} દ્વારા નોટિફિકેશન થોભાવવામાં આવ્યા}=2{{mode} અને અન્ય એક મોડ દ્વારા નોટિફિકેશન થોભાવવામાં આવ્યા}one{{mode} અને અન્ય # મોડ દ્વારા નોટિફિકેશન થોભાવવામાં આવ્યા}other{{mode} અને અન્ય # મોડ દ્વારા નોટિફિકેશન થોભાવવામાં આવ્યા}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"હવે શરૂ કરો"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"હૅડ ટ્રૅકિંગ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"રિંગર મોડ બદલવા માટે ટૅપ કરો"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"રિંગર મોડ"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"મ્યૂટ કરો"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"અનમ્યૂટ કરો"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"વાઇબ્રેટ"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"વિભાજિત સ્ક્રીનનો ઉપયોગ કરતી વખતે ડાબી બાજુની કે ઉપરની ઍપ પર સ્વિચ કરો"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"વિભાજિત સ્ક્રીન દરમિયાન: એક ઍપને બીજી ઍપમાં બદલો"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"સક્રિય વિન્ડોને ડિસ્પ્લેની વચ્ચે ખસેડો"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"વિંડોને ડાબી બાજુ ખસેડો"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"વિંડોને જમણી બાજુ ખસેડો"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"વિંડો મોટી કરો"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"વિંડો નાની કરો"</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>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"સિસ્ટમના નિયંત્રણો"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"સિસ્ટમ ઍપ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"એકથી વધુ કાર્યો કરવા"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"તાજેતરની ઍપ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"સ્ક્રીનને વિભાજિત કરો"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ઇનપુટ"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ઍપ શૉર્ટકટ"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"કીબોર્ડ શૉર્ટકટ"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"કીબોર્ડ શૉર્ટકટને કસ્ટમાઇઝ કરો"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"શું શૉર્ટકટ કાઢી નાખીએ?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"પાછા ડિફૉલ્ટ પર રીસેટ કરીએ?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"શૉર્ટકટ સોંપવા માટે કી દબાવો"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"આ તમારા કસ્ટમ શૉર્ટકટને કાયમી રીતે ડિલીટ કરશે."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"આ તમારા બધા કસ્ટમ શૉર્ટકટને કાયમ માટે ડિલીટ કરશે."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"શૉર્ટકટ શોધો"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"કોઈ શોધ પરિણામો નથી"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\'નાનું કરો\'નું આઇકન"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ઍક્શન અથવા મેટા કીનું આઇકન"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"પ્લસનું આઇકન"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"કસ્ટમાઇઝ કરો"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"થઈ ગયું"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\'મોટું કરો\'નું આઇકન"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"અથવા"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"વત્તા"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ફૉરવર્ડ સ્લૅશ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ઑબ્જેક્ટ ખેંચવાનું હૅન્ડલ"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"કીબોર્ડના સેટિંગ"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"શૉર્ટકટ સેટ કરો"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"કાઢી નાખો"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"હા, રીસેટ કરો"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"રદ કરો"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"કી દબાવો"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"કી સંયોજન પેહલેથી ઉપયોગમાં છે. અન્ય કી અજમાવી જુઓ."</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"તમારા કીબોર્ડ પરની ઍક્શન કી દબાવો"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"વાહ!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"તમે \'બધી ઍપ જુઓ\' સંકેત પૂર્ણ કર્યો"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index fafb045..d2f6e5a 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"किसी विजेट से कोई ऐप्लिकेशन खोलने के लिए, आपको अपनी पहचान की पुष्टि करनी होगी. ध्यान रखें कि आपके टैबलेट के लॉक होने पर भी, कोई व्यक्ति विजेट देख सकता है. ऐसा हो सकता है कि कुछ विजेट, लॉक स्क्रीन पर दिखाने के लिए न बने हों. इन्हें लॉक स्क्रीन पर जोड़ना असुरक्षित हो सकता है."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ठीक है"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"विजेट"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"लॉक स्क्रीन पर शॉर्टकट के तौर पर विजेट जोड़ने के लिए, पक्का करें कि सेटिंग में यह सुविधा चालू हो."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"उपयोगकर्ता बदलें"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेन्यू"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"इस सेशन के सभी ऐप्लिकेशन और डेटा को हटा दिया जाएगा."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"सूचनाएं"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"बातचीत"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"बिना आवाज़ की सभी सूचनाएं हटाएं"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'परेशान न करें\' सुविधा के ज़रिए कुछ समय के लिए सूचनाएं दिखाना रोक दिया गया है"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{कोई सूचना नहीं है}=1{{mode} की वजह से सूचना नहीं दिख रही है}=2{{mode} और एक अन्य मोड की वजह से सूचना नहीं दिख रही है}one{{mode} और # अन्य मोड की वजह से सूचना नहीं दिख रही है}other{{mode} और # अन्य मोड के की वजह से सूचनाएं नहीं दिख रही है}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"अभी शुरू करें"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"हेड ट्रैकिंग चालू है"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"रिंगर मोड बदलने के लिए टैप करें"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"रिंगर मोड"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्यूट करें"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"अनम्यूट करें"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"वाइब्रेशन की सुविधा चालू करें"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"स्प्लिट स्क्रीन पर, बाईं ओर या ऊपर के ऐप पर स्विच करने के लिए"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"स्प्लिट स्क्रीन के दौरान: एक ऐप्लिकेशन को दूसरे ऐप्लिकेशन से बदलें"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"ऐक्टिव विंडो को एक से दूसरे डिसप्ले पर स्विच करें"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"विंडो को बाईं ओर ले जाएं"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"विंडो को दाईं ओर ले जाएं"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"विंडो को बड़ा करें"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"विंडो को छोटा करें"</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>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"सिस्टम से जुड़े कंट्रोल"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"सिस्टम के ऐप्लिकेशन"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"मल्टीटास्किंग (एक साथ कई काम करना)"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"स्प्लिट स्क्रीन"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"इनपुट"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ऐप शॉर्टकट"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"कीबोर्ड शॉर्टकट"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"कीबोर्ड शॉर्टकट को पसंद के मुताबिक बनाएं"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"क्या आपको शॉर्टकट हटाना है?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"शॉर्टकट असाइन करने के लिए बटन दबाएं"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ऐसा करने से, आपका कस्टम शॉर्टकट हमेशा के लिए मिट जाएगा."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"शॉर्टकट खोजें"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"खोज का कोई नतीजा नहीं मिला"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"छोटा करने का आइकॉन"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ऐक्शन या मेटा बटन का आइकॉन"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"प्लस का आइकॉन"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"पसंद के मुताबिक बनाएं"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"हो गया"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"बड़ा करने का आइकॉन"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"या"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"प्लस"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"फ़ॉरवर्ड स्लैश"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"खींचकर छोड़ने वाला हैंडल"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"कीबोर्ड सेटिंग"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"शॉर्टकट सेट करें"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"हटाएं"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"रद्द करें"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"बटन दबाएं"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"बटन का यह कॉम्बिनेशन पहले से इस्तेमाल किया जा रहा है. कोई दूसरा कॉम्बिनेशन आज़माएं."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"अपने कीबोर्ड पर ऐक्शन बटन दबाएं"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"बहुत खूब!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"अब आपको हाथ के जेस्चर का इस्तेमाल करके, सभी ऐप्लिकेशन देखने का तरीका पता चल गया है"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index d1be021..e566e4e 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da biste otvorili aplikaciju pomoću widgeta, trebate potvrditi da ste to vi. Također napominjemo da ih svatko može vidjeti, čak i ako je vaš tablet zaključan. Neki widgeti možda nisu namijenjeni za zaključani zaslon, pa ih možda nije sigurno dodati ovdje."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Shvaćam"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgeti"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Da biste dodali widgete na zaključani zaslon kao prečac, provjerite jesu li omogućeni u postavkama."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Da biste dodali prečac Widgeti, provjerite je li u postavkama omogućena opcija Prikaži widgete na zaključanom zaslonu."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Postavke"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Promjena korisnika"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući izbornik"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Izbrisat će se sve aplikacije i podaci u ovoj sesiji."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Obavijesti"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Razgovori"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Izbriši sve bešumne obavijesti"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Značajka Ne uznemiravaj pauzirala je Obavijesti"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nema obavijesti}=1{Obavijesti je pauzirao način {mode}}=2{Obavijesti su pauzirali način {mode} i još jedan način}one{Obavijesti su pauzirali način {mode} i još # način rada}few{Obavijesti su pauzirali način {mode} i još # načina rada}other{Obavijesti su pauzirali način {mode} i još # načina rada}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Pokreni"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Praćenje glave"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Dodirnite da biste promijenili način softvera zvona"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"način softvera zvona"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključivanje zvuka"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključivanje zvuka"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string>
@@ -856,7 +861,7 @@
<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">"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_access_system_app_shortcuts" msgid="8562482996626694026">"Prikaz prečaca"</string>
<string name="group_system_go_back" msgid="2730322046244918816">"Natrag"</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>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Prelazak 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="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Premještanje aktivnog prozora između zaslona"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Premještanje prozora ulijevo"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Premještanje prozora udesno"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maksimiziranje prozora"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimiziranje prozora"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Unos"</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>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Kontrole sustava"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplikacije sustava"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Obavljanje više zadataka"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Nedavne aplikacije"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Podijeljeni zaslon"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Unos"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Prečaci aplikacija"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tipkovni prečaci"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prilagodba tipkovnih prečaca"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Želite li ukloniti prečac?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Želite li vratiti na zadano?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Pritisnite tipku da biste dodijelili prečac"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Time će se vaš prilagođeni prečac trajno izbrisati."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Time će se trajno izbrisati svi vaši prilagođeni prečaci."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečaci za pretraživanje"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nema rezultata pretraživanja"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za sažimanje"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona tipke za radnju odnosno meta tipka"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona plusa"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagodi"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotovo"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"kosa crta"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Marker za povlačenje"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Postavke tipkovnice"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Postavite prečac"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Ukloni"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Da, vrati na zadano"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Odustani"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pritisnite tipku"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinacija tipki već se upotrebljava. Pokušajte s drugom tipkom."</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite tipku za radnju na tipkovnici"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Izvrsno!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Napravili ste pokret za prikaz svih aplikacija"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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">"Upravljanje uređajima"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 4c5c364..0cbf0aa 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ha modul használatával szeretne megnyitni egy alkalmazást, igazolnia kell a személyazonosságát. Ne felejtse továbbá, hogy bárki megtekintheti a modulokat, még akkor is, amikor zárolva van a táblagép. Előfordulhat, hogy bizonyos modulokat nem a lezárási képernyőn való használatra terveztek, ezért nem biztonságos a hozzáadásuk."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Értem"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Modulok"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Ha szeretné gyorsparancsként hozzáadni a Modulok a lezárási képernyőn funkciót, győződjön meg arról, hogy a funkció engedélyezve van a beállításokban."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Felhasználóváltás"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"lehúzható menü"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"A munkamenetben található összes alkalmazás és adat törlődni fog."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Értesítések"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Beszélgetések"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Az összes néma értesítés törlése"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Ne zavarjanak funkcióval szüneteltetett értesítések"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nincs értesítés}=1{A(z) {mode} szüneteltette az értesítéseket}=2{A(z) {mode} és egy másik mód szüneteltette az értesítéseket}other{A(z) {mode} és # másik mód szüneteltette az értesítéseket}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Indítás most"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Fejkövetés"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Koppintson a csengés módjának módosításához"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"csengés módja"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"némítás"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"némítás feloldása"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"rezgés"</string>
@@ -875,14 +882,10 @@
<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>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Osztott képernyőn: az egyik alkalmazás lecserélése egy másikra"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Aktív ablak áthelyezése egyik kijelzőről a másikra"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Ablak balra mozgatása"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Ablak mozgatása jobbra"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Ablak teljes méretre állítása"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Ablak kis méretre állítása"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Bevitel"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Váltás a következő nyelvre"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Váltás az előző nyelvre"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Rendszervezérlők"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Rendszeralkalmazások"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Legutóbbi alkalmazások"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Osztott képernyő"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Bevitel"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Alkalmazásikonok"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Billentyűparancsok"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"A billentyűparancsok személyre szabása"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Eltávolítja a billentyűparancsot?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Nyomja meg a billentyűt a billentyűparancs hozzárendeléséhez"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ezzel véglegesen törli az egyéni billentyűparancsot."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Billentyűparancsok keresése"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nincsenek keresési találatok"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Összecsukás ikon"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Művelet vagy Meta billentyű ikonja"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Pluszikon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Személyre szabás"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Kész"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kibontás ikon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vagy"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plusz"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"törtvonal"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Fogópont"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Billentyűzetbeállítások"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Billentyűparancs beállítása"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Eltávolítás"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Mégse"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Nyomja le a billentyűt"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"A billentyűkombináció már használatban van. Próbálkozzon másik billentyűvel."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Nyomja meg a műveletbillentyűt az érintőpadon."</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Szép munka!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Teljesítette az összes alkalmazás megtekintésének kézmozdulatát."</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"A billentyűzet háttérvilágítása"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Fényerő: %2$d/%1$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Otthon vezérlése"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 0656700..867e0da 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Վիջեթի միջոցով հավելված բացելու համար դուք պետք է հաստատեք ձեր ինքնությունը։ Նաև նկատի ունեցեք, որ ցանկացած ոք կարող է դիտել վիջեթները, նույնիսկ երբ ձեր պլանշետը կողպված է։ Որոշ վիջեթներ կարող են նախատեսված չլինել ձեր կողպէկրանի համար, և այստեղ դրանց ավելացնելը կարող է վտանգավոր լինել։"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Եղավ"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Վիջեթներ"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Վիջեթները կողպէկրանին որպես դյուրանցում ավելացնելու համար համոզվեք, որ գործառույթը միացված է կարգավորումներում։"</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Անջատել օգտվողին"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"իջնող ընտրացանկ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Այս աշխատաշրջանի բոլոր հավելվածներն ու տվյալները կջնջվեն:"</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Ծանուցումներ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Զրույցներ"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Ջնջել բոլոր անձայն ծանուցումները"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Ծանուցումները չեն ցուցադրվի «Չանհանգստացնել» ռեժիմում"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Ծանուցումներ չկան}=1{Ծանուցումները դադարեցվել են «{mode}» ռեժիմի կողմից}=2{Ծանուցումները դադարեցվել են «{mode}» ու ևս մի ռեժիմի կողմից}one{Ծանուցումները դադարեցվել են «{mode}» ու ևս # ռեժիմի կողմից}other{Ծանուցումները դադարեցվել են «{mode}» ու ևս # ռեժիմի կողմից}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Սկսել հիմա"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Գլխի շարժումների հետագծում"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Հպեք՝ զանգակի ռեժիմը փոխելու համար"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"զանգակի ռեժիմ"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"անջատել ձայնը"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"միացնել ձայնը"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"միացնել թրթռոցը"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Անցեք աջ կողմի կամ վերևի հավելվածին տրոհված էկրանի միջոցով"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Տրոհված էկրանի ռեժիմում մեկ հավելվածը փոխարինել մյուսով"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Տեղափոխել ակտիվ պատուհանը էկրանների միջև"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Պատուհանը տեղափոխել ձախ"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Պատուհանը տեղափոխել աջ"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Ծավալել պատուհանը"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Ծալել պատուհանը"</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>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Համակարգի կառավարման տարրեր"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Համակարգային հավելվածներ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Բազմախնդրություն"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Վերջին օգտագործած հավելվածները"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Տրոհված էկրան"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ներածում"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Հավելվածի դյուրանցումներ"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Ստեղնային դյուրանցումներ"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Կարգավորեք ստեղնային դյուրանցումներ"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Հեռացնե՞լ դյուրանցումը"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Սեղմեք որևէ ստեղն՝ դյուրանցում նշանակելու համար"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ձեր հատուկ դյուրանցումն ընդմիշտ կջնջվի։"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Դյուրանցումների որոնում"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Որոնման արդյունքներ չկան"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ծալել պատկերակը"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Գործողության կամ Meta ստեղնի պատկերակ"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Պլյուս պատկերակ"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Կարգավորել"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Պատրաստ է"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ծավալել պատկերակը"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"կամ"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"գումարած"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"շեղ գիծ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Տեղափոխման նշիչ"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ստեղնաշարի կարգավորումներ"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ստեղծել դյուրանցում"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Հեռացնել"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Չեղարկել"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Սեղմեք որևէ ստեղն"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Ստեղների համակցությունն արդեն օգտագործվում է։ Ընտրեք այլ ստեղն։"</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Սեղմեք գործողության ստեղնը ստեղնաշարի վրա"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Հիանալի՛ է"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Դուք սովորեցիք բոլոր հավելվածները դիտելու ժեստը"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index da12167..06ecd11 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Untuk membuka aplikasi menggunakan widget, Anda perlu memverifikasi diri Anda. Selain itu, harap ingat bahwa siapa saja dapat melihatnya, bahkan saat tablet Anda terkunci. Beberapa widget mungkin tidak dirancang untuk layar kunci Anda dan mungkin tidak aman untuk ditambahkan di sini."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Oke"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widget"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Untuk menambahkan Widget di layar kunci sebagai pintasan, pastikan Widget diaktifkan di setelan."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Beralih pengguna"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu pulldown"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua aplikasi dan data dalam sesi ini akan dihapus."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifikasi"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Percakapan"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Hapus semua notifikasi senyap"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifikasi dijeda oleh mode Jangan Ganggu"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Tidak ada notifikasi}=1{Notifikasi dijeda oleh {mode}}=2{Notifikasi dijeda oleh {mode} dan satu mode lainnya}other{Notifikasi dijeda oleh {mode} dan # mode lainnya}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Mulai sekarang"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Pelacakan Gerak Kepala"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Ketuk untuk mengubah mode pendering"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"mode pendering"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"Tanpa suara"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktifkan"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"getar"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Beralih ke aplikasi di bagian kiri atau atas saat menggunakan layar terpisah"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Dalam layar terpisah: ganti salah satu aplikasi dengan yang lain"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Memindahkan jendela aktif dari satu layar ke layar lainnya"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Memindahkan jendela ke kiri"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Memindahkan jendela ke kanan"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Memaksimalkan jendela"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Meminimalkan jendela"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Beralih ke bahasa berikutnya"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Beralih ke bahasa sebelumnya"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Kontrol sistem"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplikasi sistem"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Aplikasi terbaru"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Layar terpisah"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Pintasan aplikasi"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Pintasan keyboard"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Menyesuaikan pintasan keyboard"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Hapus pintasan?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Tekan tombol untuk menetapkan pintasan"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Tindakan ini akan menghapus pintasan kustom Anda secara permanen."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Telusuri pintasan"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Tidak ada hasil penelusuran"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon ciutkan"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikon tombol Tindakan atau Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikon plus"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Sesuaikan"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Selesai"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon luaskan"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"garis miring"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handel geser"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Setelan Keyboard"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Setel pintasan"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Hapus"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Batal"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tekan tombol"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinasi tombol sudah digunakan. Coba tombol lain."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tekan tombol tindakan di keyboard"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Oke!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Anda telah menyelesaikan gestur untuk melihat semua aplikasi"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Lampu latar keyboard"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Tingkat %1$d dari %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrol Rumah"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index cee3ef4..4f6ff47 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Þú þarft að staðfesta að þetta sért þú til að geta opnað forrit með græju. Hafðu einnig í huga að hver sem er getur skoðað þær, jafnvel þótt spjaldtölvan sé læst. Sumar græjur eru hugsanlega ekki ætlaðar fyrir lásskjá og því gæti verið óöruggt að bæta þeim við hér."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ég skil"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Græjur"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Til að bæta „Græjur á lásskjá“ við sem flýtileið skaltu ganga úr skugga um að kveikt sé á eiginleikanum í stillingunum."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Gakktu úr skugga um að kveikt sé á „Sýna græjur á lásskjá“ til að geta bætt flýtileiðinni „Græjur“ við."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Stillingar"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skipta um notanda"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Fellivalmynd"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Öllum forritum og gögnum í þessari lotu verður eytt."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Tilkynningar"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Samtöl"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Hreinsa allar þöglar tilkynningar"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Hlé gert á tilkynningum þar sem stillt er á „Ónáðið ekki“"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Engar tilkynningar}=1{Hlé gert á tilkynningum þar sem stillt er á {mode}}=2{Hlé gert á tilkynningum þar sem stillt er á {mode} og eina aðra stillingu}one{Hlé gert á tilkynningum þar sem stillt er á {mode} og # aðra stillingu}other{Hlé gert á tilkynningum þar sem stillt er á {mode} og # aðrar stillingar}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Byrja núna"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Rakning höfuðhreyfinga"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Ýta til að skipta um hringjarastillingu"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"hringistilling"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"þagga"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"hætta að þagga"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"titringur"</string>
@@ -1415,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Kerfisstýringar"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Kerfisforrit"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Fjölvinnsla"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Nýleg forrit"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Skjáskipting"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Innsláttur"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Flýtileiðir forrita"</string>
@@ -1424,14 +1428,18 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Flýtilyklar"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Sérsníddu flýtilykla"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Fjarlægja flýtileið?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Endurstilla á sjálfgefið?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Ýttu á lykil til að stilla flýtileið"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Þetta eyðir sérsniðnu flýtileiðinni varanlega."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Þetta verður til þess að öllum sérsniðnu flýtileiðunum þínum verður eytt varanlega."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Leita að flýtileiðum"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Engar leitarniðurstöður"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Minnka tákn"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Tákn lýsilykils (aðgerðarlykils)"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plústákn"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Sérsníða"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Lokið"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Stækka tákn"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eða"</string>
@@ -1441,6 +1449,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Stillingar lyklaborðs"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Stilltu flýtileið"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Fjarlægja"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Já, endurstilla"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Hætta við"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Ýttu á lykil"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Lyklasamsetningin er þegar í notkun. Prófaðu annan lykil."</string>
@@ -1472,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Ýttu á aðgerðalykilinn á lyklaborðinu"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Vel gert!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Þú framkvæmdir bendinguna „Sjá öll forrit“"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index bc6f262..76e3360 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Per aprire un\'app utilizzando un widget, dovrai verificare la tua identità. Inoltre tieni presente che chiunque può vederlo, anche quando il tablet è bloccato. Alcuni widget potrebbero non essere stati progettati per la schermata di blocco e potrebbe non essere sicuro aggiungerli qui."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ok"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widget"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Per aggiungere Widget alla schermata di blocco come scorciatoia, assicurati che sia abilitata nelle impostazioni."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Per aggiungere la scorciatoia \"Widget\", assicurati che l\'opzione \"Mostra widget sulla schermata di blocco\" sia abilitata nelle impostazioni."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Impostazioni"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambio utente"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu a discesa"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tutte le app e i dati di questa sessione verranno eliminati."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifiche"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversazioni"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Cancella tutte le notifiche silenziose"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notifiche messe in pausa in base alla modalità Non disturbare"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nessuna notifica}=1{Notifica messa in pausa da {mode}}=2{Notifiche messe in pausa da {mode} e un\'altra modalità}many{Notifiche messe in pausa da {mode} e # di modalità}other{Notifiche messe in pausa da {mode} e altre # modalità}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Avvia adesso"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Rilev. movim. testa"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tocca per cambiare la modalità della suoneria"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"modalità suoneria"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenzia"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"riattiva l\'audio"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrazione"</string>
@@ -1415,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Controlli di sistema"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"App di sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"App recenti"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Schermo diviso"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Scorciatoie app"</string>
@@ -1424,14 +1428,18 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Scorciatoie da tastiera"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizza scorciatoie da tastiera"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Rimuovere scorciatoia?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vuoi ripristinare il valore predefinito?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Premi un tasto per assegnare una scorciatoia"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"La scorciatoia personalizzata verrà eliminata definitivamente."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Tutte le tue scorciatoie personalizzate verranno eliminate definitivamente."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Scorciatoie per la ricerca"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nessun risultato di ricerca"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona Comprimi"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icona tasto Azione o Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icona Più"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizza"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Fine"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona Espandi"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oppure"</string>
@@ -1441,6 +1449,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Impostazioni tastiera"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Imposta scorciatoia"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Rimuovi"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sì, ripristina"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annulla"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Premi un tasto"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Combinazione di tasti già in uso. Prova con un altro tasto."</string>
@@ -1472,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Premi il tasto azione sulla tastiera"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Ben fatto!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Hai completato il gesto Visualizza tutte le app."</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 31f3e4e..3257e14 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"כדי לפתוח אפליקציה באמצעות ווידג\'ט, עליך לאמת את זהותך. בנוסף, כדאי לזכור שכל אחד יכול לראות את הווידג\'טים גם כשהטאבלט שלך נעול. יכול להיות שחלק מהווידג\'טים לא נועדו למסך הנעילה ושלא בטוח להוסיף אותם לכאן."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"הבנתי"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ווידג\'טים"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"כדי להוסיף את קיצור דרך \"ווידג\'טים במסך הנעילה\", צריך לוודא שהתכונה הזו מופעלת בהגדרות."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"החלפת משתמש"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"תפריט במשיכה למטה"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"כל האפליקציות והנתונים בסשן הזה יימחקו."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"התראות"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"שיחות"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ניקוי כל ההתראות השקטות"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"התראות הושהו על ידי מצב \'נא לא להפריע\'"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{אין התראות}=1{ההתראות הושהו על ידי {mode}}=2{ההתראות הושהו על ידי {mode} ועל ידי מצב אחד נוסף}one{ההתראות הושהו על ידי {mode} ועל ידי # מצבים נוספים}other{ההתראות הושהו על ידי {mode} ועל ידי # מצבים נוספים}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"כן, אפשר להתחיל"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"מעקב ראש"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"יש להקיש כדי לשנות את מצב תוכנת הצלצול"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"מצב תוכנת הצלצול"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"השתקה"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ביטול ההשתקה"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"רטט"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"מעבר לאפליקציה מימין או למעלה בזמן שימוש במסך מפוצל"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"כשהמסך מפוצל: החלפה בין אפליקציה אחת לאחרת"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"העברת החלון הפעיל בין מסכים"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"העברת החלון שמאלה"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"העברת החלון ימינה"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"הגדלת החלון"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"מזעור החלון"</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>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"הגדרות המערכת"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"אפליקציות מערכת"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ריבוי משימות"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"אפליקציות שהיו בשימוש לאחרונה"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"מסך מפוצל"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"קלט"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"קיצורי דרך של אפליקציות"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"מקשי קיצור"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"התאמה אישית של מקשי הקיצור"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"להסיר את קיצור הדרך?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"צריך להקיש על מקש כדי להקצות מקש קיצור"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"קיצור הדרך יימחק באופן סופי."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"קיצורי דרך לחיפוש"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"אין תוצאות חיפוש"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"סמל הכיווץ"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"סמל מקש הפעולה (\"מטא\")"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"סמל הפלוס"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"התאמה אישית"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"סיום"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"סמל ההרחבה"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"או"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"סימן חיבור (פלוס)"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"קו נטוי"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"נקודת האחיזה לגרירה"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"הגדרות המקלדת"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"הגדרה של מקש קיצור"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"הסרה"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ביטול"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"יש ללחוץ על מקש"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"שילוב המקשים הזה כבר בשימוש. אפשר לנסות מקש אחר."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"צריך להקיש על מקש הפעולה במקלדת"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"כל הכבוד!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"סיימת לתרגל את התנועה להצגת כל האפליקציות"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index d5fb1a7..13dd954 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ウィジェットを使用してアプリを起動するには、本人確認が必要です。タブレットがロックされた状態でも他のユーザーにウィジェットが表示されますので、注意してください。一部のウィジェットについてはロック画面での使用を想定していないため、ロック画面への追加は危険な場合があります。"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ウィジェット"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"ロック画面にウィジェットをショートカットとして追加するには、設定でウィジェットが有効になっていることをご確認ください。"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"[ウィジェット] ショートカットを追加するには、設定で [ロック画面でのウィジェットの表示] が有効になっていることを確認してください。"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"設定"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ユーザーを切り替える"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"プルダウン メニュー"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"このセッションでのアプリとデータはすべて削除されます。"</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"通知"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"会話"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"サイレント通知がすべて消去されます"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"サイレント モードにより通知は一時停止中です"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{通知なし}=1{{mode} により通知は一時停止中です}=2{{mode} と他 1 個のモードにより通知は一時停止中です}other{{mode} と他 # 個のモードにより通知は一時停止中です}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"今すぐ開始"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ヘッド トラッキング"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"タップすると、着信音のモードを変更できます"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"着信音のモード"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ミュート"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ミュートを解除"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"バイブレーション"</string>
@@ -1415,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"システム コントロール"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"システムアプリ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"マルチタスク"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"最近使ったアプリ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"分割画面"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"入力"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"アプリのショートカット"</string>
@@ -1424,14 +1428,17 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"キーボード ショートカット"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"キーボード ショートカットをカスタマイズする"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ショートカットを削除しますか?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"デフォルトにリセットしますか?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ショートカットを割り当てるキーを押してください"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"この操作を行うと、カスタム ショートカットが完全に削除されます。"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"この操作を行うと、すべてのカスタム ショートカットが完全に削除されます。"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"検索ショートカット"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"検索結果がありません"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"閉じるアイコン"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"アクションキーまたはメタキーのアイコン"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"プラスアイコン"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"カスタマイズ"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"リセット"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"完了"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"開くアイコン"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"または"</string>
@@ -1441,6 +1448,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"キーボードの設定"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ショートカットの設定"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"削除"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"リセットする"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"キャンセル"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"キーを押してください"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"このキーの組み合わせはすでに使用されています。別のキーを試してください。"</string>
@@ -1472,6 +1480,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"キーボードのアクションキーを押します"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"完了です!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"「すべてのアプリを表示する」ジェスチャーを学習しました"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 8bbb181..9390bd2 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"უნდა დაადასტუროთ თქვენი ვინაობა, რათა გახსნათ აპი ვიჯეტის გამოყენებით. გაითვალისწინეთ, რომ ნებისმიერს შეუძლია მათი ნახვა, მაშინაც კი, როცა ტაბლეტი დაბლოკილია. ზოგი ვიჯეტი შეიძლება არ იყოს გათვლილი თქვენი დაბლოკილი ეკრანისთვის და მათი აქ დამატება შეიძლება სახიფათო იყოს."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"გასაგებია"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ვიჯეტები"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"ჩაკეტილ ეკრანზე ვიჯეტის მალსახმობის სახით დასამატებლად დარწმუნდით, რომ ის ჩართულია პარამეტრებიდან."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"„ვიჯეტების“ მალსახმობის დასამატებლად დარწმუნდით, რომ პარამეტრებში ჩართულია „დაბლოკილ ეკრანზე ვიჯეტების ჩვენება“."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"პარამეტრები"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"მომხმარებლის გადართვა"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ჩამოშლადი მენიუ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ამ სესიის ყველა აპი და მონაცემი წაიშლება."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"შეტყობინებები"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"საუბრები"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ყველა ჩუმი შეტყობინების გასუფთავება"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"შეტყობინებები დაპაუზდა „არ შემაწუხოთ“ რეჟიმის მეშვეობით"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{შეტყობინებები არ არის}=1{შეტყობინებები შეჩერებულია {mode}-ის გამო}=2{შეტყობინებები შეჩერებულია {mode}-ის და ერთი სხვა რეჟიმის გამო}other{შეტყობინებები შეჩერებულია {mode}-ის და # სხვა რეჟიმის გამო}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"დაწყება ახლავე"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ხმის მიდევნებით"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"შეეხეთ მრეკავის რეჟიმის შესაცვლელად"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"მრეკავის რეჟიმი"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"დადუმება"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"დადუმების მოხსნა"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ვიბრაცია"</string>
@@ -1415,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"სისტემის მართვის საშუალებები"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"სისტემის აპები"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"მრავალამოცანიანი რეჟიმი"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"ბოლოდროინდელი აპები"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"ეკრანის გაყოფა"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"შეყვანა"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"აპის მალსახმობები"</string>
@@ -1424,14 +1428,18 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"კლავიატურის მალსახმობები"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"კლავიატურის მალსახმობების მორგება"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"გსურთ მალსახმობის წაშლა?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"გსურთ ნაგულისხმევზე გადაყენება?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"მალსახმობის მინიჭებისთვის დააჭირეთ კლავიშს"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ეს თქვენს მორგებულ მალსახმობებს სამუდამოდ წაშლის."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ეს სამუდამოდ წაშლის თქვენს ყველა მორგებულ მალსახმობს."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ძიების მალსახმობები"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ძიების შედეგები არ არის"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ხატულის ჩაკეცვა"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"მოქმედების ან მეტა კლავიშის ხატულა"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"პლუსის ხატულა"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"მორგება"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"მზადაა"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ხატულის გაფართოება"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ან"</string>
@@ -1441,6 +1449,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"კლავიატურის პარამეტრები"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"მალსახმობის დაყენება"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ამოშლა"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"დიახ, გადაყენდეს"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"გაუქმება"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"დააჭირეთ კლავიშს"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"კლავიშების კომბინაცია უკვე გამოიყენება. ცადეთ სხვა კლავიში."</string>
@@ -1472,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"დააჭირეთ მოქმედების კლავიშს თქვენს კლავიატურაზე"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ყოჩაღ!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"თქვენ დაასრულეთ ყველა აპის ნახვის ჟესტი"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 4f11293..672bb66 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Қолданбаны виджет көмегімен ашу үшін жеке басыңызды растауыңыз керек. Сондай-ақ басқалар оларды планшетіңіз құлыптаулы кезде де көре алатынын ескеріңіз. Кейбір виджеттер құлып экранына арналмаған болады, сондықтан оларды мұнда қосу қауіпсіз болмауы мүмкін."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Түсінікті"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виджеттер"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"To add Widgets on the lock screen as a shortcut, make sure it is enabled in settings."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"Виджеттер\" таңбашасын қосу үшін параметрлерде \"Виджеттерді құлыптаулы экранда көрсету\" опциясының қосулы екенін тексеріңіз."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Параметрлер"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Пайдаланушыны ауыстыру"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ашылмалы мәзір"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Осы сеанстағы барлық қолданба мен дерек жойылады."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Хабарландырулар"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Әңгімелер"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Барлық үнсіз хабарландыруларды өшіру"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Хабарландырулар Мазаламау режимінде кідіртілді"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Хабарландырулар жоқ.}=1{Хабарландыруларды {mode} режимі кідіртті.}=2{Хабарландыруларды {mode} және тағы бір режим кідіртті.}other{Хабарландыруларды {mode} және тағы # режим кідіртті.}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Қазір бастау"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Бас қимылын қадағалау"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Қоңырау режимін өзгерту үшін түртіңіз."</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"қоңырау режимі"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"дыбысын өшіру"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"дыбысын қосу"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"дірілдету"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Бөлінген экранда сол не жоғары жақтағы қолданбаға ауысу"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Экранды бөлу кезінде: бір қолданбаны басқасымен алмастыру"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Дисплейлер арасында қосулы терезені ауыстыру"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Терезені сол жаққа жылжыту"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Терезені оң жаққа жылжыту"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Терезені ұлғайту"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Терезені кішірейту"</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>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Жүйені басқару элементтері"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Жүйелік қолданбалар"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Мультитаскинг"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Соңғы қолданбалар"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Экранды бөлу"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Енгізу"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Қолданба таңбашалары"</string>
@@ -1428,25 +1428,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Перне тіркесімдері"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Пернелер тіркесімін бейімдеу"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Жылдам пәрменді өшіру керек пе?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Әдепкі таңбашаларға қайтару керек пе?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Жылдам пәрменді тағайындау үшін пернені басыңыз."</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Арнаулы жылдам пәрменіңіз біржола жойылады."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Мұндайда барлық арнаулы таңбашалар біржола жойылады."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Іздеу жылдам пәрмендері"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Іздеу нәтижелері жоқ."</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жию белгішесі"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Әрекет немесе Meta пернесінің белгішесі"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Қосу белгішесі"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Бейімдеу"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Бастапқы күйге қайтару"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Дайын"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жаю белгішесі"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"немесе"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"қосу"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"қиғаш сызық"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Сүйрейтін тетік"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Пернетақта параметрлері"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Жылдам пәрменді орнату"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Өшіру"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Иә, қайтару"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Бас тарту"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Пернені басыңыз"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Бұл пернелер тіркесімі қазір қолданыста. Басқа перне таңдаңыз."</string>
@@ -1478,6 +1480,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Пернетақтадағы әрекет пернесін басыңыз."</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Жарайсыз!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Барлық қолданбаны көру қимылын орындадыңыз."</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 33aeb70..96a9ed4 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ដើម្បីបើកកម្មវិធីដោយប្រើធាតុក្រាហ្វិក អ្នកនឹងត្រូវផ្ទៀងផ្ទាត់ថាជាអ្នក។ ទន្ទឹមនឹងនេះ សូមចងចាំថា នរណាក៏អាចមើលធាតុក្រាហ្វិកបាន សូម្បីពេលថេប្លេតរបស់អ្នកជាប់សោក៏ដោយ។ ធាតុក្រាហ្វិកមួយចំនួនប្រហែលមិនត្រូវបានរចនាឡើងសម្រាប់អេក្រង់ចាក់សោរបស់អ្នកទេ និងមិនមានសុវត្ថិភាពឡើយ បើបញ្ចូលទៅទីនេះ។"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"យល់ហើយ"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ធាតុក្រាហ្វិក"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"ដើម្បីបញ្ចូលធាតុក្រាហ្វិកនៅលើអេក្រង់ចាក់សោជាផ្លូវកាត់ សូមប្រាកដថាវាត្រូវបានបើកនៅក្នុងការកំណត់។"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"ដើម្បីបញ្ចូលផ្លូវកាត់ \"ធាតុក្រាហ្វិក\" ត្រូវប្រាកដថា \"បង្ហាញធាតុក្រាហ្វិកនៅលើអេក្រង់ចាក់សោ\" ត្រូវបានបើកនៅក្នុងការកំណត់។"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ការកំណត់"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ប្ដូរអ្នកប្រើ"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ម៉ឺនុយទាញចុះ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"កម្មវិធី និងទិន្នន័យទាំងអស់ក្នុងវគ្គនេះនឹងត្រូវលុប។"</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ការជូនដំណឹង"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"ការសន្ទនា"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"សម្អាតការជូនដំណឹងស្ងាត់ទាំងអស់"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"ការជូនដំណឹងបានផ្អាកដោយមុខងារកុំរំខាន"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{គ្មានការជូនដំណឹង}=1{ការជូនដំណឹងត្រូវបានផ្អាកដោយ {mode}}=2{ការជូនដំណឹងត្រូវបានផ្អាកដោយ {mode} និងមុខងារមួយផ្សេងទៀត}other{ការជូនដំណឹងត្រូវបានផ្អាកដោយ {mode} និងមុខងារ # ផ្សេងទៀត}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"ចាប់ផ្ដើមឥឡូវ"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"រេតាមក្បាល"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ចុចដើម្បីប្ដូរមុខងាររោទ៍"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"មុខងារកម្មវិធីរោទ៍"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"បិទសំឡេង"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"បើកសំឡេង"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ញ័រ"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ប្ដូរទៅកម្មវិធីនៅខាងឆ្វេង ឬខាងលើ ពេលកំពុងប្រើមុខងារបំបែកអេក្រង់"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"ក្នុងអំឡុងពេលប្រើមុខងារបំបែកអេក្រង់៖ ជំនួសកម្មវិធីពីមួយទៅមួយទៀត"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"ផ្លាស់ទីវិនដូដែលសកម្មរវាងផ្ទាំងអេក្រង់"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"ផ្លាស់ទីវិនដូទៅឆ្វេង"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"ផ្លាស់ទីវិនដូទៅស្ដាំ"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"ពង្រីកវិនដូ"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"បង្រួមវិនដូ"</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>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"ការគ្រប់គ្រងប្រព័ន្ធ"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"កម្មវិធីប្រព័ន្ធ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ការធ្វើកិច្ចការច្រើន"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"កម្មវិធីថ្មីៗ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"មុខងារបំបែកអេក្រង់"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ធាតុចូល"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ផ្លូវកាត់កម្មវិធី"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"ផ្លូវកាត់ក្ដារចុច"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ប្ដូរផ្លូវកាត់ក្ដារចុចតាមបំណង"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ដកផ្លូវកាត់ចេញឬ?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"កំណត់ឡើងវិញទៅលំនាំដើមឬ?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ចុចគ្រាប់ចុច ដើម្បីកំណត់ផ្លូវកាត់"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ការធ្វើបែបនេះនឹងលុបផ្លូវកាត់ផ្ទាល់ខ្លួនរបស់អ្នកជាអចិន្ត្រៃយ៍។"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"សកម្មភាពនេះនឹងលុបផ្លូវកាត់ផ្ទាល់ខ្លួនរបស់អ្នកទាំងអស់ជាអចិន្ត្រៃយ៍។"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ស្វែងរកផ្លូវកាត់"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"គ្មានលទ្ធផលស្វែងរកទេ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"រូបតំណាង \"បង្រួម\""</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"រូបគ្រាប់ចុចសកម្មភាព ឬមេតា"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"រូបសញ្ញាបូក"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ប្ដូរតាមបំណង"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"រួចរាល់"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"រូបតំណាង \"ពង្រីក\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ឬ"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"បូក"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"សញ្ញា (/)"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ដងអូស"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ការកំណត់ក្ដារចុច"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"កំណត់ផ្លូវកាត់"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ដកចេញ"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"បាទ/ចាស កំណត់ឡើងវិញ"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"បោះបង់"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ចុចគ្រាប់ចុច"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"កំពុងប្រើបន្សំគ្រាប់ចុចស្រាប់ហើយ។ សាកល្បងប្រើគ្រាប់ចុចផ្សេង។"</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ចុចគ្រាប់ចុចសកម្មភាពលើក្ដារចុចរបស់អ្នក"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ធ្វើបានល្អ!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"អ្នកបានបញ្ចប់ចលនាមើលកម្មវិធីទាំងអស់ហើយ"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index ee06f23..89b8db2 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ವಿಜೆಟ್ ಅನ್ನು ಬಳಸಿಕೊಂಡು ಆ್ಯಪ್ ತೆರೆಯಲು, ಇದು ನೀವೇ ಎಂದು ನೀವು ದೃಢೀಕರಿಸಬೇಕಾಗುತ್ತದೆ. ಅಲ್ಲದೆ, ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಲಾಕ್ ಆಗಿದ್ದರೂ ಸಹ ಯಾರಾದರೂ ಅವುಗಳನ್ನು ವೀಕ್ಷಿಸಬಹುದು ಎಂಬುದನ್ನು ನೆನಪಿನಲ್ಲಿಡಿ. ಕೆಲವು ವಿಜೆಟ್ಗಳು ನಿಮ್ಮ ಲಾಕ್ ಸ್ಕ್ರೀನ್ಗಾಗಿ ಉದ್ದೇಶಿಸದೇ ಇರಬಹುದು ಮತ್ತು ಇಲ್ಲಿ ಸೇರಿಸುವುದು ಸುರಕ್ಷಿತವಲ್ಲದಿರಬಹುದು."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ಅರ್ಥವಾಯಿತು"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ವಿಜೆಟ್ಗಳು"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"ಶಾರ್ಟ್ಕಟ್ನಂತೆ ಲಾಕ್ ಸ್ಕ್ರೀನ್ನಲ್ಲಿ ವಿಜೆಟ್ಗಳನ್ನು ಸೇರಿಸಲು, ಅದನ್ನು ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆಯೇ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"ವಿಜೆಟ್ಗಳು\" ಶಾರ್ಟ್ಕಟ್ ಸೇರಿಸಲು, ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ \"ಲಾಕ್ ಸ್ಕ್ರೀನ್ನಲ್ಲಿ ವಿಜೆಟ್ಗಳನ್ನು ತೋರಿಸಿ\" ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ಬಳಕೆದಾರರನ್ನು ಬದಲಿಸಿ"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ಪುಲ್ಡೌನ್ ಮೆನು"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ಈ ಸೆಶನ್ನಲ್ಲಿನ ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ಅಧಿಸೂಚನೆಗಳು"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"ಸಂಭಾಷಣೆಗಳು"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ಎಲ್ಲಾ ನಿಶ್ಶಬ್ಧ ಅಧಿಸೂಚನೆಗಳನ್ನು ತೆರವುಗೊಳಿಸಿ"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಎನ್ನುವ ಮೂಲಕ ಅಧಿಸೂಚನೆಗಳನ್ನು ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{ಯಾವುದೇ ನೋಟಿಫಿಕೇಶನ್ಗಳು ಇಲ್ಲ}=1{ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು {mode} ವಿರಾಮಗೊಳಿಸಿದೆ}=2{ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು {mode} ಹಾಗೂ ಇತರ ಒಂದು ಮೋಡ್ ವಿರಾಮಗೊಳಿಸಿವೆ}one{ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು {mode} ಹಾಗೂ ಇತರ # ಮೋಡ್ಗಳು ವಿರಾಮಗೊಳಿಸಿವೆ}other{ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು {mode} ಹಾಗೂ ಇತರ # ಮೋಡ್ಗಳು ವಿರಾಮಗೊಳಿಸಿವೆ}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"ಈಗ ಪ್ರಾರಂಭಿಸಿ"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ಹೆಡ್ ಟ್ರ್ಯಾಕಿಂಗ್"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ರಿಂಗರ್ ಮೋಡ್ ಬದಲಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ರಿಂಗರ್ ಮೋಡ್"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ಮ್ಯೂಟ್ ಮಾಡಿ"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ಅನ್ಮ್ಯೂಟ್ ಮಾಡಿ"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ವೈಬ್ರೇಟ್"</string>
@@ -1415,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"ಸಿಸ್ಟಂ ನಿಯಂತ್ರಣಗಳು"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"ಸಿಸ್ಟಂ ಆ್ಯಪ್ಗಳು"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ಮಲ್ಟಿಟಾಸ್ಕಿಂಗ್"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"ಇತ್ತೀಚಿನ ಆ್ಯಪ್ಗಳು"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ಇನ್ಪುಟ್"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ಆ್ಯಪ್ ಶಾರ್ಟ್ಕಟ್ಗಳು"</string>
@@ -1424,14 +1428,17 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್ಕಟ್ಗಳು"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಕಸ್ಟಮೈಸ್ ಮಾಡಿ"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ಶಾರ್ಟ್ಕಟ್ ಅನ್ನು ತೆಗೆದುಹಾಕಬೇಕೇ?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ಡೀಫಾಲ್ಟ್ಗೆ ರೀಸೆಟ್ ಮಾಡಬೇಕೆ?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ಶಾರ್ಟ್ಕಟ್ ಅನ್ನು ನಿಯೋಜಿಸಲು ಕೀಯನ್ನು ಒತ್ತಿರಿ"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ಇದು ನಿಮ್ಮ ಕಸ್ಟಮ್ ಶಾರ್ಟ್ಕಟ್ ಅನ್ನು ಶಾಶ್ವತವಾಗಿ ಅಳಿಸುತ್ತದೆ."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ಇದು ನಿಮ್ಮ ಎಲ್ಲಾ ಕಸ್ಟಮ್ ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಶಾಶ್ವತವಾಗಿ ಅಳಿಸುತ್ತದೆ."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ಹುಡುಕಾಟದ ಶಾರ್ಟ್ಕಟ್ಗಳು"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ಯಾವುದೇ ಹುಡುಕಾಟ ಫಲಿತಾಂಶಗಳಿಲ್ಲ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ಕುಗ್ಗಿಸುವ ಐಕಾನ್"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ಆ್ಯಕ್ಷನ್ ಅಥವಾ ಮೆಟಾ ಕೀ ಐಕಾನ್"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ಪ್ಲಸ್ ಐಕಾನ್"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ಕಸ್ಟಮೈಸ್ ಮಾಡಿ"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"ರೀಸೆಟ್ ಮಾಡಿ"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ಮುಗಿದಿದೆ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ವಿಸ್ತೃತಗೊಳಿಸುವ ಐಕಾನ್"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ಅಥವಾ"</string>
@@ -1441,6 +1448,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ಕೀಬೋರ್ಡ್ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ಶಾರ್ಟ್ಕಟ್ ಸೆಟ್ ಮಾಡಿ"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ತೆಗೆದುಹಾಕಿ"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ಹೌದು, ರೀಸೆಟ್ ಮಾಡಿ"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ರದ್ದುಮಾಡಿ"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ಕೀ ಅನ್ನು ಒತ್ತಿರಿ"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"ಕೀ ಸಂಯೋಜನೆಯು ಈಗಾಗಲೇ ಬಳಕೆಯಲ್ಲಿದೆ. ಮತ್ತೊಂದು ಕೀ ಬಳಸಿ ನೋಡಿ."</string>
@@ -1472,6 +1480,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ನಿಮ್ಮ ಕೀಬೋರ್ಡ್ನಲ್ಲಿ ಆ್ಯಕ್ಷನ್ ಕೀಯನ್ನು ಒತ್ತಿ"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ಭೇಷ್!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"ನೀವು ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳ ಜೆಸ್ಚರ್ ವೀಕ್ಷಣೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 6dcf92c..276d708 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"위젯을 사용하여 앱을 열려면 본인 인증을 해야 합니다. 또한 태블릿이 잠겨 있더라도 누구나 볼 수 있다는 점을 유의해야 합니다. 일부 위젯은 잠금 화면에 적합하지 않고 여기에 추가하기에 안전하지 않을 수 있습니다."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"확인"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"위젯"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"잠금 화면에 위젯을 바로가기로 추가하려면 설정에서 위젯을 사용 설정하세요."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\'위젯\' 바로가기를 추가하려면 설정에서 \'잠금 화면에 위젯 표시\'가 사용 설정되어 있어야 합니다."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"설정"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"사용자 전환"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"풀다운 메뉴"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"알림"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"대화"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"무음 알림 모두 삭제"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"방해 금지 모드로 알림이 일시중지됨"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{알림 없음}=1{{mode} 모드로 인해 알림이 일시중지되었습니다.}=2{{mode} 및 다른 모드로 인해 알림이 일시중지되었습니다.}other{{mode} 및 다른 모드 #개로 인해 알림이 일시중지되었습니다.}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"시작하기"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"머리 추적"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"탭하여 벨소리 장치 모드 변경"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"벨소리 장치 모드"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"음소거"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"음소거 해제"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"진동"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"화면 분할을 사용하는 중에 왼쪽 또는 위쪽에 있는 앱으로 전환하기"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"화면 분할 중: 다른 앱으로 바꾸기"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"디스플레이 간 활성 창 이동"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"창을 왼쪽으로 이동"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"창을 오른쪽으로 이동"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"창 최대화"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"창 최소화"</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>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"시스템 컨트롤"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"시스템 앱"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"멀티태스킹"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"최근 앱"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"화면 분할"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"입력"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"앱 단축키"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"단축키"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"단축키 맞춤설정"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"바로가기를 제거하시겠습니까?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"기본값으로 재설정하시겠어요?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"키를 눌러 단축키 지정"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"맞춤 단축어가 영구적으로 삭제됩니다."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"모든 맞춤 바로가기가 완전히 삭제됩니다."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"검색 바로가기"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"검색 결과 없음"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"접기 아이콘"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"작업 또는 메타 키 아이콘"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"더하기 아이콘"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"맞춤설정"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"완료"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"확장 아이콘"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"또는"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"더하기"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"슬래시"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"드래그 핸들"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"키보드 설정"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"단축키 설정"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"삭제"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"재설정"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"취소"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"키를 누르세요."</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"이미 사용 중인 키 조합입니다. 다른 키를 사용해 보세요."</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"키보드의 작업 키를 누르세요."</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"잘하셨습니다"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"모든 앱 보기 동작을 완료했습니다."</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 5a58507..ab20a7b 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Колдонмону виджет аркылуу ачуу үчүн өзүңүздү ырасташыңыз керек. Алар кулпуланган планшетиңизде да көрүнүп турат. Кээ бир виджеттерди кулпуланган экранда колдоно албайсыз, андыктан аларды ал жерге кошпой эле койгонуңуз оң."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Түшүндүм"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виджеттер"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"\"Кулпуланган экрандагы виджеттер\" функциясын ыкчам баскыч катары кошуу үчүн параметрлерге өтүп, анын иштетилгенин текшериңиз."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"Виджеттер\" ыкчам баскычын кошуу үчүн параметрлерге өтүп, \"Виджеттерди кулпуланган экранда көрсөтүү\" параметри иштетилгенин текшериңиз."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Параметрлер"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Колдонуучуну которуу"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ылдый түшүүчү меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Бул сеанстагы бардык колдонмолор жана аларга байланыштуу нерселер өчүрүлөт."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Билдирмелер"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Сүйлөшүүлөр"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Бардык үнсүз билдирмелерди өчүрүү"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\"Тынчымды алба\" режиминде билдирмелер тындырылды"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Билдирмелер жок}=1{{mode} режими билдирмелерди тындырды}=2{{mode} жана дагы бир режим билдирмелерди тындырды}other{{mode} жана дагы # режим билдирмелерди тындырды}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Азыр баштоо"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Баштын кыймылына көз салуу"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Коңгуроо режимин өзгөртүү үчүн басыңыз"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"коңгуроо режими"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"үнсүз"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"үнүн чыгаруу"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"дирилдөө"</string>
@@ -1415,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Системанын башкаруу элементтери"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системанын колдонмолору"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Бир нече тапшырма аткаруу"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Акыркы колдонмолор"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Экранды бөлүү"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Киргизүү"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Колдонмонун ыкчам баскычтары"</string>
@@ -1424,14 +1428,18 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Ыкчам баскычтар"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Ыкчам баскычтарды ыңгайлаштыруу"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Ыкчам баскыч өчүрүлсүнбү?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Баштапкы абалга келтиресизби?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Ыкчам баскычты дайындоо үчүн баскычты басыңыз"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ушуну менен жеке ыкчам баскычыңыз биротоло өчүрүлөт."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Ушуну менен жеке ыкчам баскычтарыңыздын баары биротоло өчүрүлөт."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Ыкчам баскычтарды издөө"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Эч нерсе табылган жок"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жыйыштыруу сүрөтчөсү"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Аракет же Мета ачкыч сүрөтчөсү"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Кошуу сүрөтчөсү"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Ыңгайлаштыруу"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Бүттү"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жайып көрсөтүү сүрөтчөсү"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"же"</string>
@@ -1441,6 +1449,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Баскычтоп параметрлери"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ыкчам баскычты тууралоо"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Өчүрүү"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ооба, баштапкы абалга келтирилсин"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Баскычты басыңыз"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Ачкычтардын айкалышы колдонулууда. Башка ачкычты байкап көрүңүз."</string>
@@ -1472,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Баскычтобуңуздагы аракет баскычын басыңыз"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Эң жакшы!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Бардык колдонмолорду көрүү жаңсоосун аткардыңыз"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 1cfd68e..a3a25e8 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ເພື່ອເປີດແອັບໂດຍໃຊ້ວິດເຈັດ, ທ່ານຈະຕ້ອງຢັ້ງຢືນວ່າແມ່ນທ່ານ. ນອກຈາກນັ້ນ, ກະລຸນາຮັບຊາບວ່າທຸກຄົນສາມາດເບິ່ງຂໍ້ມູນດັ່ງກ່າວໄດ້, ເຖິງແມ່ນວ່າແທັບເລັດຂອງທ່ານຈະລັອກຢູ່ກໍຕາມ. ວິດເຈັດບາງຢ່າງອາດບໍ່ໄດ້ມີໄວ້ສຳລັບໜ້າຈໍລັອກຂອງທ່ານ ແລະ ອາດບໍ່ປອດໄພທີ່ຈະເພີ່ມໃສ່ບ່ອນນີ້."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ເຂົ້າໃຈແລ້ວ"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ວິດເຈັດ"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"ເພື່ອເພີ່ມວິດເຈັດຢູ່ໜ້າຈໍລັອກເປັນທາງລັດ, ໃຫ້ແນ່ໃຈວ່າມັນຖືກເປີດການນຳໃຊ້ໃນການຕັ້ງຄ່າ."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ສະຫຼັບຜູ້ໃຊ້"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ເມນູແບບດຶງລົງ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ແອັບຯແລະຂໍ້ມູນທັງໝົດໃນເຊດຊັນນີ້ຈະຖືກລຶບອອກ."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ການແຈ້ງເຕືອນ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"ການສົນທະນາ"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ລຶບລ້າງການແຈ້ງເຕືອນແບບງຽບທັງໝົດ"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"ຢຸດການແຈ້ງເຕືອນໂດຍໂໝດຫ້າມລົບກວນແລ້ວ"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{ບໍ່ມີການແຈ້ງເຕືອນ}=1{{mode} ຢຸດການແຈ້ງເຕືອນໄວ້ຊົ່ວຄາວ}=2{{mode} ແລະ ອີກ 1 ໂໝດຢຸດການແຈ້ງເຕືອນໄວ້ຊົ່ວຄາວ}other{{mode} ແລະ ອີກ # ໂໝດຢຸດການແຈ້ງເຕືອນໄວ້ຊົ່ວຄາວ}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"ເລີ່ມດຽວນີ້"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ການຕິດຕາມຫົວ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ແຕະເພື່ອປ່ຽນໂໝດຣິງເກີ"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ໂໝດຣິງເກີ"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ປິດສຽງ"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ເຊົາປິດສຽງ"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ສັ່ນເຕືອນ"</string>
@@ -1415,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"ການຄວບຄຸມລະບົບ"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"ແອັບລະບົບ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ການເຮັດຫຼາຍໜ້າວຽກພ້ອມກັນ"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"ແອັບຫຼ້າສຸດ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"ແບ່ງໜ້າຈໍ"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ອິນພຸດ"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ທາງລັດແອັບ"</string>
@@ -1424,14 +1430,20 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"ຄີລັດ"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ປັບແຕ່ງຄີລັດ"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ລຶບທາງລັດອອກບໍ?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ກົດປຸ່ມເພື່ອກຳນົດທາງລັດ"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ການດຳເນີນການນີ້ຈະລຶບທາງລັດທີ່ກຳນົດເອງຂອງທ່ານຢ່າງຖາວອນ."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ທາງລັດການຊອກຫາ"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ບໍ່ມີຜົນການຊອກຫາ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ໄອຄອນຫຍໍ້ລົງ"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ໄອຄອນຄຳສັ່ງ ຫຼື ປຸ່ມ Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ໄອຄອນໝາຍບວກ"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ປັບແຕ່ງ"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ແລ້ວໆ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ໄອຄອນຂະຫຍາຍ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ຫຼື"</string>
@@ -1441,6 +1453,8 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ການຕັ້ງຄ່າແປ້ນພິມ"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ຕັ້ງທາງລັດ"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ລຶບອອກ"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ຍົກເລີກ"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ກົດປຸ່ມ"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"ນໍາໃຊ້ປຸ່ມປະສົມຢູ່ແລ້ວ. ໃຫ້ລອງປຸ່ມອື່ນ."</string>
@@ -1472,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ກົດປຸ່ມຄຳສັ່ງຢູ່ແປ້ນພິມຂອງທ່ານ"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ດີຫຼາຍ!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"ທ່ານເບິ່ງທ່າທາງຂອງແອັບທັງໝົດສຳເລັດແລ້ວ"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 3c81e37..cba0db1 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Kad galėtumėte atidaryti programą naudodami valdiklį, turėsite patvirtinti savo tapatybę. Be to, atminkite, kad bet kas gali peržiūrėti valdiklius net tada, kai planšetinis kompiuteris užrakintas. Kai kurie valdikliai gali būti neskirti jūsų užrakinimo ekranui ir gali būti nesaugu juos čia pridėti."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Supratau"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Valdikliai"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Jei norite pridėti valdiklių šaukinį užrakinimo ekrane, įsitikinkite, kad tai įgalinta nustatymuose."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Perjungti naudotoją"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"išplečiamasis meniu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bus ištrintos visos šios sesijos programos ir duomenys."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Pranešimai"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Pokalbiai"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Išvalyti visus tylius pranešimus"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Pranešimai pristabdyti naudojant netrukdymo režimą"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nėra pranešimų}=1{Pranešimai pristabdyti naudojant režimą „{mode}“}=2{Pranešimai pristabdyti naudojant režimą „{mode}“ ir dar vieną režimą}one{Pranešimai pristabdyti naudojant režimą „{mode}“ ir dar # režimą}few{Pranešimai pristabdyti naudojant režimą „{mode}“ ir dar # režimus}many{Pranešimai pristabdyti naudojant režimą „{mode}“ ir dar # režimo}other{Pranešimai pristabdyti naudojant režimą „{mode}“ ir dar # režimų}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Pradėti dabar"</string>
@@ -704,6 +709,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Galvos stebėjimas"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Palieskite, kad pakeistumėte skambučio režimą"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"skambučio režimas"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, palieskite, kad pakeistumėte skambučio režimą"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"nutildyti"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"įjungti garsą"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibruoti"</string>
@@ -1415,7 +1421,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Sistemos valdikliai"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistemos programos"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Kelių užduočių atlikimas"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Naujausios programos"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Išskaidyto ekrano režimas"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Įvestis"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Programos spartieji klavišai"</string>
@@ -1424,14 +1429,20 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Spartieji klavišai"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Sparčiųjų klavišų tinkinimas"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Pašalinti spartųjį klavišą?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Paspauskite klavišą, kad priskirtumėte spartųjį klavišą"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Bus visam laikui ištrintas tinkintas spartusis klavišas."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Ieškoti sparčiųjų klavišų"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nėra jokių paieškos rezultatų"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sutraukimo piktograma"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Veiksmo arba metaduomenų klavišo piktograma"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Pliuso piktograma"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Tinkinti"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Atlikta"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Išskleidimo piktograma"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"arba"</string>
@@ -1441,6 +1452,8 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatūros nustatymai"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Nustatyti spartųjį klavišą"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Pašalinti"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Atšaukti"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Paspauskite klavišą"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Klavišų derinys jau naudojamas. Bandykite naudoti kitą klavišą."</string>
@@ -1472,6 +1485,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Paspauskite klaviatūros veiksmų klavišą"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Puikiai padirbėta!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Atlikote visų programų peržiūros gestą"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Mokomoji animacija. Spustelėkite, kad pristabdytumėte ir tęstumėte atkūrimą."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatūros foninis apšvietimas"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d lygis iš %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Namų sistemos valdymas"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 5b553d9..7698008 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Lai atvērtu lietotni, izmantojot logrīku, jums būs jāapstiprina sava identitāte. Turklāt ņemiet vērā, ka ikviens var skatīt logrīkus, pat ja planšetdators ir bloķēts. Iespējams, daži logrīki nav paredzēti izmantošanai bloķēšanas ekrānā, un var nebūt droši tos šeit pievienot."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Labi"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Logrīki"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Lai varētu pievienot funkciju “Logrīki bloķēšanas ekrānā” kā saīsni, iestatījumos noteikti iespējojiet šo funkciju."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mainīt lietotāju"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"novelkamā izvēlne"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tiks dzēstas visas šīs sesijas lietotnes un dati."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Paziņojumi"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Sarunas"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Notīrīt visus klusos paziņojumus"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Paziņojumi pārtraukti, izmantojot iestatījumu “Netraucēt”"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nav paziņojumu}=1{Paziņojumu rādīšana ir pārtraukta režīma “{mode}” dēļ}=2{Paziņojumu rādīšana ir pārtraukta režīma “{mode}” un vēl viena režīma dēļ}zero{Paziņojumu rādīšana ir pārtraukta režīma “{mode}” un vēl # režīmu dēļ}one{Paziņojumu rādīšana ir pārtraukta režīma “{mode}” un vēl # režīma dēļ}other{Paziņojumu rādīšana ir pārtraukta režīma “{mode}” un vēl # režīmu dēļ}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Sākt tūlīt"</string>
@@ -704,6 +709,8 @@
<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>
<string name="volume_ringer_mode" msgid="6867838048430807128">"zvanītāja režīms"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"izslēgt skaņu"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ieslēgt skaņu"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrēt"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Pāriet uz lietotni pa kreisi/augšā, kamēr izmantojat sadalīto ekrānu."</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Ekrāna sadalīšanas režīmā: pārvietot lietotni no viena ekrāna uz otru"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Pārvietot aktīvo logu starp displejiem"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Pārvietot logu pa kreisi"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Pārvietot logu pa labi"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maksimizēt logu"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimizēt logu"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Ievade"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Pārslēgt uz nākamo valodu"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Pārslēgt uz iepriekšējo valodu"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Sistēmas vadīklas"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistēmas lietotnes"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Vairākuzdevumu režīms"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Pēdējās izmantotās lietotnes"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ekrāna sadalīšana"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ievade"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Lietotņu saīsnes"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Īsinājumtaustiņi"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Īsinājumtaustiņu pielāgošana"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Vai noņemt saīsni?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Lai piešķirtu īsinājumtaustiņu, nospiediet taustiņu"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Veicot šo darbību, jūsu pielāgotā saīsne tiks neatgriezeniski izdzēsta."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Meklēt saīsnes"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nav meklēšanas rezultātu"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sakļaušanas ikona"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Darbību jeb meta taustiņa ikona"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Pluszīmes ikona"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Pielāgot"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gatavs"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Izvēršanas ikona"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vai"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"uz priekšu vērstā slīpsvītra"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Vilkšanas turis"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastatūras iestatījumi"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Iestatīt īsinājumtaustiņu"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Noņemt"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Atcelt"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Nospiediet taustiņu"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Taustiņu kombinācija jau tiek izmantota. Izmēģiniet citu taustiņu."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tastatūrā nospiediet darbību taustiņu."</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Lieliski!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Jūs sekmīgi veicāt visu lietotņu skatīšanas žestu."</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastatūras fona apgaismojums"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Līmenis numur %1$d, kopā ir %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Mājas kontrolierīces"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 32ee791..783e52e 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"За да отворите апликација со помош на виџет, ќе треба да потврдите дека сте вие. Покрај тоа, имајте предвид дека секој може да ги гледа виџетите, дури и кога вашиот таблет е заклучен. Некои виџети можеби не се наменети за вашиот заклучен екран, па можеби не е безбедно да се додадат овде."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Сфатив"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виџети"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"За да ја додадете „Виџети на заклучен екран“ како кратенка, проверете дали е овозможена во „Поставки“."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Промени го корисникот"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"паѓачко мени"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Сите апликации и податоци во сесијава ќе се избришат."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Известувања"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Разговори"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Избриши ги сите бесчујни известувања"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Известувањата се паузирани од „Не вознемирувај“"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Нема известувања}=1{Известувањата ги паузираше {mode}}=2{Известувањата ги паузираа {mode} и уште еден режим}one{Известувањата ги паузираа {mode} и уште # режим}other{Известувањата ги паузираа {mode} и уште # режими}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Започни сега"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Следење на главата"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Допрете за да го промените режимот на ѕвончето"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"режим на ѕвонче"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"исклучен звук"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"вклучен звук"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибрации"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Префрлете се на апликацијата лево или горе при користењето поделен екран"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"При поделен екран: префрлете ги аплик. од едната на другата страна"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Движете го активниот прозорец меѓу екраните"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Премести го прозорецот налево"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Премести го прозорецот надесно"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Максимизирај го прозорецот"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Минимизирај го прозорецот"</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>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Системски контроли"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системски апликации"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Мултитаскинг"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Неодамнешни апликации"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Поделен екран"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Внесување"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Кратенки за апликации"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Кратенки од тастатура"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Приспособете ги кратенките од тастатурата"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Да се отстрани кратенката?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Притиснете го копчето за да доделите кратенка"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ова ќе ја избрише вашата приспособена кратенка трајно."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пребарувајте кратенки"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Нема резултати од пребарување"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за собирање"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Икона за дејство или копче за дејство"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Икона плус"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Приспособете"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширување"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"плус"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"коса црта"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Рачка за влечење"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Поставки за тастатурата"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Поставете кратенка"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Отстрани"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Откажи"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Притиснете го копчето"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Комбинацијата на копчиња веќе се користи. Обидете се со друго копче."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Притиснете го копчето за дејство на тастатурата"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Браво!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Го завршивте движењето за прегледување на сите апликации"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index ed66531..3134872 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"വിജറ്റ് ഉപയോഗിച്ച് ഒരു ആപ്പ് തുറക്കാൻ, ഇത് നിങ്ങൾ തന്നെയാണെന്ന് പരിശോധിച്ചുറപ്പിക്കേണ്ടതുണ്ട്. നിങ്ങളുടെ ടാബ്ലെറ്റ് ലോക്കായിരിക്കുമ്പോഴും എല്ലാവർക്കും അത് കാണാനാകുമെന്നതും ഓർക്കുക. ചില വിജറ്റുകൾ നിങ്ങളുടെ ലോക്ക് സ്ക്രീനിന് ഉള്ളതായിരിക്കില്ല, അവ ഇവിടെ ചേർക്കുന്നത് സുരക്ഷിതവുമായിരിക്കില്ല."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"മനസ്സിലായി"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"വിജറ്റുകൾ"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"കുറുക്കുവഴിയായി ലോക്ക് സ്ക്രീനിൽ വിജറ്റുകൾ ചേർക്കാൻ, ക്രമീകരണത്തിൽ അത് പ്രവർത്തനക്ഷമമാക്കിയിട്ടുണ്ടെന്ന് ഉറപ്പാക്കുക."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"വിജറ്റുകൾ\" കുറുക്കുവഴി ചേർക്കാൻ, ക്രമീകരണത്തിൽ \"ലോക്ക് സ്ക്രീനിൽ വിജറ്റുകൾ കാണിക്കുക\" പ്രവർത്തനക്ഷമമാക്കിയെന്ന് ഉറപ്പാക്കുക."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ക്രമീകരണം"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ഉപയോക്താവ് മാറുക"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"പുൾഡൗൺ മെനു"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ഈ സെഷനിലെ എല്ലാ ആപ്പുകളും ഡാറ്റയും ഇല്ലാതാക്കും."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"അറിയിപ്പുകൾ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"സംഭാഷണങ്ങൾ"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"എല്ലാ നിശബ്ദ അറിയിപ്പുകളും മായ്ക്കുക"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'ശല്യപ്പെടുത്തരുത്\' വഴി അറിയിപ്പുകൾ താൽക്കാലികമായി നിർത്തി"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{അറിയിപ്പുകൾ ഒന്നുമില്ല}=1{{mode}, അറിയിപ്പുകൾ താൽക്കാലികമായി നിർത്തിയിരിക്കുന്നു}=2{{mode} എന്നതും മറ്റൊരു മോഡും, അറിയിപ്പുകൾ താൽക്കാലികമായി നിർത്തിയിരിക്കുന്നു}other{{mode} എന്നതും മറ്റ് # മോഡുകളും, അറിയിപ്പുകൾ താൽക്കാലികമായി നിർത്തിയിരിക്കുന്നു}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"ഇപ്പോൾ ആരംഭിക്കുക"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ഹെഡ് ട്രാക്കിംഗ്"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"റിംഗർ മോഡ് മാറ്റാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"റിംഗർ മോഡ്"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"മ്യൂട്ട് ചെയ്യുക"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"അൺമ്യൂട്ട് ചെയ്യുക"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"വൈബ്രേറ്റ് ചെയ്യുക"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"സ്ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കുമ്പോൾ ഇടതുവശത്തെ/മുകളിലെ ആപ്പിലേക്ക് മാറൂ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"സ്ക്രീൻ വിഭജന മോഡിൽ: ഒരു ആപ്പിൽ നിന്ന് മറ്റൊന്നിലേക്ക് മാറുക"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"സജീവ വിൻഡോകൾ ഡിസ്പ്ലേകൾക്ക് ഇടയിൽ നീക്കുക"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"വിൻഡോ ഇടത്തേക്ക് നീക്കുക"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"വിൻഡോ വലത്തേക്ക് നീക്കുക"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"വിൻഡോ വലുതാക്കുക"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"വിൻഡോ ചെറുതാക്കുക"</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>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"സിസ്റ്റം നിയന്ത്രണങ്ങൾ"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"സിസ്റ്റം ആപ്പുകൾ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"മൾട്ടിടാസ്കിംഗ്"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"അടുത്തിടെ ഉപയോഗിച്ച ആപ്പുകൾ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"സ്ക്രീൻ വിഭജന മോഡ്"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ഇൻപുട്ട്"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ആപ്പ് കുറുക്കുവഴികൾ"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"കീബോഡ് കുറുക്കുവഴികൾ"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"കീബോർഡ് കുറുക്കുവഴികൾ ഇഷ്ടാനുസൃതമാക്കുക"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"കുറുക്കുവഴി നീക്കം ചെയ്യണോ?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ഡിഫോൾട്ടിലേക്ക് തിരികെ റീസെറ്റ് ചെയ്യണോ?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"കുറുക്കുവഴി അസൈൻ ചെയ്യാൻ കീ അമർത്തുക"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ഇത് നിങ്ങളുടെ ഇഷ്ടാനുസൃത കുറുക്കുവഴി ശാശ്വതമായി ഇല്ലാതാക്കും."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"നിങ്ങളുടെ എല്ലാ ഇഷ്ടാനുസൃത കുറുക്കുവഴികളും ശാശ്വതമായി ഇത് ഇല്ലാതാക്കും."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"തിരയൽ കുറുക്കുവഴികൾ"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"തിരയൽ ഫലങ്ങളൊന്നുമില്ല"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ചുരുക്കൽ ഐക്കൺ"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ആക്ഷൻ/മെറ്റാ കീ ഐക്കൺ"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"പ്ലസ് ഐക്കൺ"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ഇഷ്ടാനുസൃതമാക്കുക"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"പൂർത്തിയായി"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"വികസിപ്പിക്കൽ ഐക്കൺ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"അല്ലെങ്കിൽ"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"പ്ലസ്"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ഫോർവേഡ് സ്ലാഷ്"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"വലിച്ചിടുന്നതിനുള്ള ഹാൻഡിൽ"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"കീബോർഡ് ക്രമീകരണം"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"കുറുക്കുവഴി സജ്ജീകരിക്കുക"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"നീക്കം ചെയ്യുക"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ഉവ്വ്, റീസെറ്റ് ചെയ്യുക"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"റദ്ദാക്കുക"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"കീ അമർത്തുക"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"കീ കോമ്പിനേഷൻ ഇതിനകം ഉപയോഗത്തിലുണ്ട്. മറ്റൊരു കീ പരീക്ഷിക്കുക."</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"നിങ്ങളുടെ കീബോർഡിലെ ആക്ഷൻ കീ അമർത്തുക"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"അഭിനന്ദനങ്ങൾ!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"\'എല്ലാ ആപ്പുകളും കാണുക\' ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index f2863d0..42182bf 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Виджет ашиглан аппыг нээхийн тулд та өөрийгөө мөн болохыг баталгаажуулах шаардлагатай болно. Мөн таны таблет түгжээтэй байсан ч тэдгээрийг дурын хүн үзэж болохыг санаарай. Зарим виджет таны түгжээтэй дэлгэцэд зориулагдаагүй байж магадгүй ба энд нэмэхэд аюултай байж болзошгүй."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ойлголоо"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виджет"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Түгжээтэй дэлгэц дээр товчлол байдлаар виджет нэмэхийн тулд тохиргоонд виджетийг идэвхжүүлсэн эсэхийг нягтална уу."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Хэрэглэгчийг сэлгэх"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"эвхмэл цэс"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Энэ харилцан үйлдлийн бүх апп болон дата устах болно."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Мэдэгдлүүд"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Харилцан яриа"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Бүх чимээгүй мэдэгдлийг арилгах"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Бүү саад бол горимын түр зогсоосон мэдэгдэл"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Мэдэгдэл байхгүй}=1{Мэдэгдлийг {mode} түр зогсоосон}=2{Мэдэгдлийг {mode} болон өөр нэг горим түр зогсоосон}other{Мэдэгдлийг {mode} болон өөр # горим түр зогсоосон}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Одоо эхлүүлэх"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Толгой хянах"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Хонхны горимыг өөрчлөхийн тулд товшино уу"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"хонхны горим"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"дууг хаах"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"дууг нээх"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"чичрэх"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Дэлгэц хуваахыг ашиглаж байхдаа зүүн талд эсвэл дээр байх апп руу сэлгэ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Дэлгэц хуваах үеэр: аппыг нэгээс нөгөөгөөр солих"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Идэвхтэй цонхыг дэлгэц хооронд зөөх"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Цонхыг зүүн тийш зөөх"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Цонхыг баруун тийш зөөх"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Цонхыг томруулах"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Цонхыг багасгах"</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>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Системийн тохиргоо"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системийн аппууд"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Олон ажил зэрэг хийх"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Саяхны аппууд"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Дэлгэцийг хуваах"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Оролт"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Аппын товчлол"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Товчлуурын шууд холбоос"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Товчлуурын шууд холбоосыг өөрчлөх"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Товчлолыг хасах уу?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Товчлол оноохын тулд товч дарна уу"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Энэ нь таны захиалгат товчлолыг бүрмөсөн устгана."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Товчлолууд хайх"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ямар ч хайлтын илэрц байхгүй"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Хураах дүрс тэмдэг"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Үйлдлийн товч буюу өөрөөр Мета товчийн дүрс тэмдэг"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Нэмэх дүрс тэмдэг"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Өөрчлөх"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Болсон"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Дэлгэх дүрс тэмдэг"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"эсвэл"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"болон"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"урагшаа ташуу зураас"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Чирэх бариул"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Гарын тохиргоо"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Товчлол тохируулах"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Хасах"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Цуцлах"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Товч дарна уу"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Товчийн хослолыг аль хэдийн ашиглаж байна. Өөр товч туршиж үзнэ үү."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Гар дээрх тусгай товчлуурыг дарна уу"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Сайн байна!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Та бүх аппыг харах зангааг гүйцэтгэлээ"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 35ee42f..b108ccc 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -113,7 +113,7 @@
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"एक अॅप रेकॉर्ड करा"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"पूर्ण स्क्रीन रेकॉर्ड करा"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"संपूर्ण स्क्रीन रेकॉर्ड करा: %s"</string>
- <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"तुम्ही तुमची पूर्ण स्क्रीन रेकॉर्ड करता, तेव्हा तुमच्या स्क्रीनवर दाखवलेली कोणतीही गोष्टी रेकॉर्ड केली जाते. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"तुम्ही तुमची पूर्ण स्क्रीन रेकॉर्ड करता, तेव्हा तुमच्या स्क्रीनवर दाखवलेली कोणतीही गोष्ट रेकॉर्ड केली जाते. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"तुम्ही अॅप रेकॉर्ड करता, तेव्हा त्या अॅपमध्ये दाखवलेली किंवा प्ले केलेली कोणतीही गोष्ट रेकॉर्ड केली जाते. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"स्क्रीन रेकॉर्ड करा"</string>
<string name="screenrecord_app_selector_title" msgid="3854492366333954736">"रेकॉर्ड करण्यासाठी अॅप निवडा"</string>
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"विजेट वापरून अॅप उघडण्यासाठी, तुम्हाला हे तुम्हीच असल्याची पडताळणी करावी लागेल. तसेच, लक्षात ठेवा, तुमचा टॅबलेट लॉक असतानादेखील कोणीही ती पाहू शकते. काही विजेट कदाचित तुमच्या लॉक स्क्रीनसाठी नाहीत आणि ती इथे जोडणे असुरक्षित असू शकते."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"समजले"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"विजेट"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"विजेट शॉर्टकट म्हणून लॉक स्क्रीनवर जोडण्यासाठी, सेटिंग्जमध्ये ती सुरू असल्याची खात्री करा."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"विजेट\" शॉर्टकट जोडण्यासाठी, सेटिंग्जमध्ये \"लॉक स्क्रीनवर विजेट दाखवा\" सुरू असल्याची खात्री करा."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"सेटिंग्ज"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"वापरकर्ता स्विच करा"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेनू"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"या सत्रातील सर्व अॅप्स आणि डेटा हटवला जाईल."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"सूचना"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"संभाषणे"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"सर्व सायलंट सूचना साफ करा"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"व्यत्यय आणून नकाद्वारे सूचना थांबवल्या"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{कोणतेही नोटिफिकेशन नाही}=1{{mode} द्वारे नोटिफिकेशन थांबवली आहेत}=2{{mode} द्वारे आणि आणखी एका मोडद्वारे नोटिफिकेशन थांबवली आहेत}other{{mode} द्वारे आणि आणखी # मोडद्वारे नोटिफिकेशन थांबवली आहेत}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"आता सुरू करा"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"हेड ट्रॅकिंग"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"रिंगर मोड बदलण्यासाठी टॅप करा"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"रिंगर मोड"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्यूट करा"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"म्यूट काढून टाका"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"व्हायब्रेट करा"</string>
@@ -1415,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"सिस्टीमची नियंत्रणे"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"सिस्टीम अॅप्स"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"मल्टिटास्किंग"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"अलीकडील अॅप्स"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"स्प्लिट स्क्रीन"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"इनपुट"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"अॅप शॉर्टकट"</string>
@@ -1424,14 +1428,18 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"कीबोर्ड शॉर्टकट"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"कीबोर्ड शॉर्टकट कस्टमाइझ करा"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"शॉर्टकट काढून टाकायचा आहे का?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"डीफॉल्टवर पुन्हा रीसेट करायचे आहे का?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"शॉर्टकट असाइन करण्यासाठी की प्रेस करा"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"यामुळे तुमचा कस्टम शॉर्टकट कायमचा हटवला जाईल."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"हे तुमचे सर्व कस्टम शॉर्टकट कायमचे हटवेल."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"शोधण्यासाठी शॉर्टकट"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"कोणतेही शोध परिणाम नाहीत"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"कोलॅप्स करा आयकन"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"कृती किंवा मेटा की आयकन"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"अधिक आयकन"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"कस्टमाइझ करा"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"पूर्ण झाले"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"विस्तार करा आयकन"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"किंवा"</string>
@@ -1441,6 +1449,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"कीबोर्ड सेटिंग्ज"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"शॉर्टकट सेट करा"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"काढून टाका"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"होय, रीसेट करा"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"रद्द करा"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"की प्रेस करा"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"की कॉम्बिनेशन आधीपासून वापरले जात आहे. दुसरी की वापरून पहा."</string>
@@ -1472,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"तुमच्या कीबोर्डवर अॅक्शन की प्रेस करा"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"खूप छान!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"तुम्ही ॲप्स पाहण्याचे जेश्चर पूर्ण केले आहे"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index cfb95a2..6b9d6d6 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Untuk membuka apl menggunakan widget, anda perlu mengesahkan identiti anda. Selain itu, perlu diingat bahawa sesiapa sahaja boleh melihat widget tersebut, walaupun semasa tablet anda dikunci. Sesetengah widget mungkin tidak sesuai untuk skrin kunci anda dan mungkin tidak selamat untuk ditambahkan di sini."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widget"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Untuk menambahkan Widget pada skrin kunci sebagai pintasan, pastikan skrin kunci itu didayakan dalam tetapan."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Untuk menambahkan pintasan \"Widget\", pastikan \"Tunjukkan widget pada skrin kunci\" didayakan dalam tetapan."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Tetapan"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Tukar pengguna"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu tarik turun"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua apl dan data dalam sesi ini akan dipadam."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Pemberitahuan"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Perbualan"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Kosongkan semua pemberitahuan senyap"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Pemberitahuan dijeda oleh Jangan Ganggu"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Tiada pemberitahuan}=1{Pemberitahuan dijeda oleh {mode}}=2{Pemberitahuan dijeda oleh {mode} dan satu lagi mod yang lain}other{Pemberitahuan dijeda oleh {mode} dan # mod yang lain}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Mulakan sekarang"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Penjejakan Kepala"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Ketik untuk menukar mod pendering"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"mod pendering"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"redam"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"nyahredam"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"getar"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Tukar kepada apl di sebelah kiri/atas semasa menggunakan skrin pisah"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Semasa skrin pisah: gantikan apl daripada satu apl kepada apl lain"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Alihkan tetingkap aktif antara paparan"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Alihkan tetingkap ke sebelah kiri"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Alihkan tetingkap ke sebelah kanan"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maksimumkan tetingkap"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimumkan tetingkap"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Beralih kepada bahasa seterusnya"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Beralih kepada bahasa sebelumnya"</string>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Kawalan sistem"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Apl sistem"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Berbilang tugas"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Apl terbaharu"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Skrin pisah"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Pintasan apl"</string>
@@ -1428,25 +1428,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Pintasan papan kekunci"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Sesuaikan pintasan papan kekunci"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Alih keluar pintasan?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Tetapkan kembali kepada lalai?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Tekan kekunci untuk menetapkan pintasan"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Tindakan ini akan memadamkan pintasan tersuai anda secara kekal."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Tindakan ini akan memadamkan semua pintasan tersuai anda secara kekal."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pintasan carian"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Tiada hasil carian"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kuncupkan ikon"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikon kekunci tindakan atau Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikon tambah"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Sesuaikan"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Tetapkan semula"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Selesai"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kembangkan ikon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"tambah"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"garis condong ke hadapan"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Pemegang seret"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tetapan Papan Kekunci"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Tetapkan pintasan"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Alih keluar"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ya, tetapkan semula"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Batal"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tekan kekunci"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Gabungan kekunci sudah digunakan. Cuba kekunci lain."</string>
@@ -1478,6 +1480,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tekan kekunci tindakan pada papan kekunci anda"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Syabas!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Anda telah melengkapkan gerak isyarat lihat semua apl"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 8cce371..50f75ee 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ဝိဂျက်သုံး၍ အက်ပ်ဖွင့်ရန်အတွက် သင်ဖြစ်ကြောင်း အတည်ပြုရန်လိုသည်။ ထို့ပြင် သင့်တက်ဘလက် လော့ခ်ချထားချိန်၌ပင် မည်သူမဆို ၎င်းတို့ကို ကြည့်နိုင်ကြောင်း သတိပြုပါ။ ဝိဂျက်အချို့ကို လော့ခ်မျက်နှာပြင်အတွက် ရည်ရွယ်ထားခြင်း မရှိသဖြင့် ဤနေရာတွင် ထည့်ပါက မလုံခြုံနိုင်ပါ။"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"နားလည်ပြီ"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ဝိဂျက်များ"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"လော့ခ်မျက်နှာပြင်တွင် ဖြတ်လမ်းလင့်ခ်အဖြစ် ‘ဝိဂျက်များ’ ထည့်ရန် ၎င်းကို ဆက်တင်များတွင်ဖွင့်ထားကြောင်း သေချာပါစေ။"</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"အသုံးပြုသူကို ပြောင်းလဲရန်"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ဆွဲချမီနူး"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ဒီချိတ်ဆက်မှု ထဲက အက်ပ်များ အားလုံး နှင့် ဒေတာကို ဖျက်ပစ်မည်။"</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"အကြောင်းကြားချက်များ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"စကားဝိုင်းများ"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"အသံတိတ် အကြောင်းကြားချက်များအားလုံးကို ရှင်းလင်းရန်"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"အကြောင်းကြားချက်များကို \'မနှောင့်ယှက်ရ\' က ခေတ္တရပ်ထားသည်"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{အကြောင်းကြားချက် မရှိပါ}=1{{mode} က ခဏရပ်ထားသော အကြောင်းကြားချက်များ}=2{{mode} နှင့် အခြားမုဒ်တစ်ခုက ခဏရပ်ထားသော အကြောင်းကြားချက်များ}other{{mode} နှင့် အခြားမုဒ် # ခုက ခဏရပ်ထားသော အကြောင်းကြားချက်များ}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"ယခု စတင်ပါ"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ခေါင်းလှုပ်ရှားမှု"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ဖုန်းခေါ်သံမုဒ်သို့ ပြောင်းရန် တို့ပါ"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"အသံမြည်မုဒ်"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"အသံပိတ်ရန်"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"အသံဖွင့်ရန်"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"တုန်ခါမှု"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းသုံးစဉ် ဘယ် (သို့) အထက်ရှိအက်ပ်သို့ ပြောင်းရန်"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"မျက်နှာပြင် ခွဲ၍ပြသစဉ်- အက်ပ်တစ်ခုကို နောက်တစ်ခုနှင့် အစားထိုးရန်"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"လက်ရှိဝင်းဒိုးကို ပြကွက်များအကြား ရွှေ့ခြင်း"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"ဝင်းဒိုးကို ဘယ်ဘက်ရွှေ့ရန်"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"ဝင်းဒိုးကို ညာဘက်ရွှေ့ရန်"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"ဝင်ဒိုးကို ချဲ့ရန်"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"ဝင်းဒိုးကို ချုံ့ရန်"</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>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"စနစ် ထိန်းချုပ်မှုများ"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"စနစ် အက်ပ်များ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"တစ်ပြိုင်နက် များစွာလုပ်ခြင်း"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"မကြာသေးမီက အက်ပ်များ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"မျက်နှာပြင် ခွဲ၍ပြသခြင်း"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ထည့်သွင်းမှု"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"အက်ပ်ဖြတ်လမ်းလင့်ခ်များ"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"လက်ကွက်ဖြတ်လမ်းများ"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"လက်ကွက်ဖြတ်လမ်းများကို စိတ်ကြိုက်လုပ်ခြင်း"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ဖြတ်လမ်းလင့်ခ် ဖယ်ရှားမလား။"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ဖြတ်လမ်းလင့်ခ်သတ်မှတ်ရန် ကီးကို နှိပ်ပါ"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"၎င်းသည် သင့်စိတ်ကြိုက် ဖြတ်လမ်းလင့်ခ်ကို အပြီးဖျက်ပါမည်။"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ဖြတ်လမ်းများ ရှာရန်"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ရှာဖွေမှုရလဒ် မရှိပါ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"လျှော့ပြရန် သင်္ကေတ"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"လုပ်ဆောင်ချက် (သို့) Meta ကီးသင်္ကေတ"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"အပေါင်းသင်္ကေတ"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"စိတ်ကြိုက်လုပ်ရန်"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ပြီးပြီ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ပိုပြရန် သင်္ကေတ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"သို့မဟုတ်"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"အပေါင်း"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ညာဘက်မျဉ်းစောင်း"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ဖိဆွဲအထိန်း"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ကီးဘုတ်ဆက်တင်များ"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ဖြတ်လမ်း သတ်မှတ်ရန်"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ဖယ်ရှားရန်"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"မလုပ်တော့"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ကီးကို နှိပ်ပါ"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"ကီးပေါင်းစပ်ခြင်းကို သုံးနေပြီးဖြစ်သည်။ အခြားကီးကို စမ်းကြည့်ပါ။"</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ကီးဘုတ်တွင် လုပ်ဆောင်ချက်ကီး နှိပ်ပါ"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"အလွန်ကောင်းပါသည်။"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"အက်ပ်အားလုံးကို ကြည့်ခြင်းလက်ဟန် သင်ခန်းစာပြီးပါပြီ"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index c863d19..f84353f 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"For å åpne en app ved hjelp av en modul må du bekrefte at det er deg. Husk også at hvem som helst kan se dem, selv om nettbrettet er låst. Noen moduler er kanskje ikke laget for å være på låseskjermen og kan være utrygge å legge til der."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Greit"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Moduler"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"For å legge til moduler på låseskjermen som en snarvei, sørg for at de er slått på i innstillingene."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Bytt bruker"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullegardinmeny"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apper og data i denne økten blir slettet."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Varsler"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Samtaler"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Fjern alle lydløse varsler"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Varsler er satt på pause av «Ikke forstyrr»"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Ingen varsler}=1{Varsler er satt på pause av {mode}}=2{Varsler er satt på pause av {mode} og én modus til}other{Varsler er satt på pause av {mode} og # moduser til}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Start nå"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Hodesporing"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Trykk for å endre ringemodus"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"modus for ringeprogrammet"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"kutt lyden"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå på lyden"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrer"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Bytt til appen til venstre eller over mens du bruker delt skjerm"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"I delt skjerm: Bytt ut en app"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Flytt det aktive vinduet mellom skjermer"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Flytt vinduet til venstre"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Flytt vinduet til høyre"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maksimer vinduet"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimerer vinduet"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Skrivespråk"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Bytt til neste språk"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Bytt til forrige språk"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Systemkontroller"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systemapper"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Nylige apper"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Delt skjerm"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Inndata"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App-snarveier"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Hurtigtaster"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tilpass hurtigtastene"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Vil du fjerne hurtigtasten?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Trykk på en tast for å tilordne hurtigtasten"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Dette fører til at den egendefinerte hurtigtasten slettes permanent."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snarveier til søk"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ingen søkeresultater"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Skjul-ikon"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Handlings- eller Meta-tast-ikon"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plussikon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Tilpass"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Ferdig"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vis-ikon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"pluss"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"skråstrek"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Håndtak"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastaturinnstillinger"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Angi hurtigtast"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Fjern"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Avbryt"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Trykk på tasten"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tastekombinasjonen brukes allerede. Prøv en annen tast."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Trykk på handlingstasten på tastaturet"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Bra!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Du har fullført bevegelsen for å se alle apper"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrunnslys for tastatur"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Hjemkontroller"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 681e328..8fd8367 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"विजेट प्रयोग गरी एप खोल्न तपाईंले आफ्नो पहिचान पुष्टि गर्नु पर्ने हुन्छ। साथै, तपाईंको ट्याब्लेट लक भएका बेला पनि सबै जनाले तिनलाई देख्न सक्छन् भन्ने कुरा ख्याल गर्नुहोस्। केही विजेटहरू लक स्क्रिनमा प्रयोग गर्ने उद्देश्यले नबनाइएका हुन सक्छन् र तिनलाई यहाँ हाल्नु सुरक्षित नहुन सक्छ।"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"बुझेँ"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"विजेटहरू"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"विजेटहरू लक स्क्रिनमा सर्टकटका रूपमा हाल्न सेटिङमा गई यो सुविधा अन गरिएको छ भन्ने सुनिश्चित गर्नुहोस्।"</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"प्रयोगकर्ता फेर्नुहोस्"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेनु"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"यो सत्रमा भएका सबै एपहरू र डेटा मेटाइने छ।"</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"सूचनाहरू"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"वार्तालापहरू"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"सबै मौन सूचनाहरू हटाउनुहोस्"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"बाधा नपुऱ्याउनुहोस् नामक मोडमार्फत पज पारिएका सूचनाहरू"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{कुनै पनि नोटिफिकेसन छैन}=1{{mode} ले गर्दा नोटिफिकेसनहरू पज गरिएका छन्}=2{{mode} र अन्य एक मोडले गर्दा नोटिफिकेसनहरू पज गरिएका छन्}other{{mode} र अन्य # वटा मोडले गर्दा नोटिफिकेसनहरू पज गरिएका छन्}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"अहिले न"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"हेड ट्र्याकिङ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"रिङ्गर मोड बदल्न ट्याप गर्नुहोस्"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"घण्टी बजाउने मोड"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्युट गर्नुहोस्"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"अनम्युट गर्नुहोस्"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"कम्पन गर्नुहोस्"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"स्प्लिट स्क्रिन प्रयोग गर्दै गर्दा बायाँ वा माथिको एप चलाउनुहोस्"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"स्प्लिट स्क्रिन प्रयोग गरिएका बेला: एउटा स्क्रिनमा भएको एप अर्कोमा लैजानुहोस्"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"सक्रिय विन्डोलाई एउटा डिस्प्लेबाट सारेर अर्को डिस्प्लेमा लैजानुहोस्"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"विन्डो सारेर बायाँतिर लैजानुहोस्"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"विन्डो सारेर दायाँतिर लैजानुहोस्"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"विन्डो म्याक्सिमाइज गर्नुहोस्"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"विन्डो मिनिमाइज गर्नुहोस्"</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>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"सिस्टमसँग सम्बन्धित नियन्त्रणहरू"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"सिस्टम एपहरू"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"एकै पटक एकभन्दा बढी एप चलाउन मिल्ने सुविधा"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"हालसालै चलाइएका एपहरू"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"स्प्लिट स्क्रिन"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"इनपुट"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"एपका सर्टकटहरू"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"किबोर्डका सर्टकटहरू"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"किबोर्डका सर्टकटहरू कस्टमाइज गर्नुहोस्"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"सर्टकट हटाउने हो?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"सर्टकट असाइन गर्न की थिच्नुहोस्"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"यसो गर्नुभयो भने तपाईंको कस्टम सर्टकट सदाका लागि मेटिने छ।"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"खोजका सर्टकटहरू"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"कुनै पनि खोज परिणाम भेटिएन"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\"कोल्याप्स गर्नुहोस्\" आइकन"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"एक्सन वा Meta कीको आइकन"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"प्लस आइकन"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"कस्टमाइज गर्नुहोस्"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"पूरा भयो"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\"एक्स्पान्ड गर्नुहोस्\" आइकन"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"वा"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"प्लस"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"फर्वार्ड स्ल्यास"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ड्र्याग ह्यान्डल"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"किबोर्डसम्बन्धी सेटिङ"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"सर्टकट सेट गर्नुहोस्"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"हटाउनुहोस्"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"रद्द गर्नुहोस्"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"की थिच्नुहोस्"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"यो की कम्बिनेसन प्रयोग गरिसकिएको छ। अर्कै की प्रयोग गरी हेर्नुहोस्।"</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"आफ्नो किबोर्डमा भएको एक्सन की थिच्नुहोस्"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"स्याबास!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"तपाईंले जेस्चर प्रयोग गरी सबै एपहरू हेर्ने तरिका सिक्नुभएको छ"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index c5c08b1..d03b5fb 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Als je een app wilt openen met een widget, moet je verifiëren dat jij het bent. Houd er ook rekening mee dat iedereen ze kan bekijken, ook als je tablet vergrendeld is. Bepaalde widgets zijn misschien niet bedoeld voor je vergrendelscherm en kunnen hier niet veilig worden toegevoegd."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Als je Widgets als sneltoets wilt toevoegen aan het vergrendelscherm, zorg je dat deze is aangezet in de instellingen."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Als je de snelkoppeling Widgets wilt toevoegen, zorg je dat Widgets tonen op het vergrendelingsscherm aanstaat in de instellingen."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Instellingen"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Gebruiker wijzigen"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pull-downmenu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps en gegevens in deze sessie worden verwijderd."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Meldingen"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Gesprekken"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Alle stille meldingen wissen"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Meldingen onderbroken door \'Niet storen\'"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Geen meldingen}=1{Meldingen onderbroken door {mode}}=2{Meldingen onderbroken door {mode} en 1 andere modus}other{Meldingen onderbroken door {mode} en # andere modi}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Nu starten"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Hoofdtracking"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tik om de beltoonmodus te wijzigen"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"beltoonmodus"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"geluid uit"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"geluid aanzetten"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"trillen"</string>
@@ -875,14 +880,10 @@
<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="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Actief venster verplaatsen tussen schermen"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Venster naar links verplaatsen"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Venster naar rechts verplaatsen"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Venster maximaliseren"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Venster minimaliseren"</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>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Overschakelen naar vorige taal"</string>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Systeemopties"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systeem-apps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Recente apps"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Gesplitst scherm"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Invoer"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App-sneltoetsen"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Sneltoetsen"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Sneltoetsen aanpassen"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Sneltoets verwijderen?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Resetten naar standaard?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Druk op de toets om de sneltoets toe te wijzen"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Hiermee wordt je aangepaste sneltoets definitief verwijderd."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Hiermee worden al je aangepaste snelkoppelingen definitief verwijderd."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sneltoetsen zoeken"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Geen zoekresultaten"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icoon voor samenvouwen"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icoon voor actie- of metatoets"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plusicoon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Aanpassen"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Klaar"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icoon voor uitvouwen"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"slash"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handgreep voor slepen"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Toetsenbordinstellingen"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Sneltoets instellen"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Verwijderen"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ja, resetten"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annuleren"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Druk op een toets"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Toetsencombinatie is al in gebruik. Probeer een andere toets."</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Druk op de actietoets op het toetsenbord"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Goed gedaan!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Je weet nu hoe je het gebaar Alle apps bekijken maakt"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Achtergrondverlichting van toetsenbord"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d van %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Bediening voor in huis"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index b7d840a..91900e7 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ଏକ ୱିଜେଟ ବ୍ୟବହାର କରି ଗୋଟିଏ ଆପ ଖୋଲିବା ପାଇଁ ଏହା ଆପଣ ଅଟନ୍ତି ବୋଲି ଆପଣଙ୍କୁ ଯାଞ୍ଚ କରିବାକୁ ହେବ। ଆହୁରି ମଧ୍ୟ, ଆପଣଙ୍କ ଟାବଲେଟ ଲକ ଥିଲେ ମଧ୍ୟ ଯେ କୌଣସି ବ୍ୟକ୍ତି ଏହାକୁ ଭ୍ୟୁ କରିପାରିବେ ବୋଲି ମନେ ରଖନ୍ତୁ। କିଛି ୱିଜେଟ ଆପଣଙ୍କ ଲକ ସ୍କ୍ରିନ ପାଇଁ ଉଦ୍ଦିଷ୍ଟ ହୋଇନଥାଇପାରେ ଏବଂ ଏଠାରେ ଯୋଗ କରିବା ଅସୁରକ୍ଷିତ ହୋଇପାରେ।"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ବୁଝିଗଲି"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ୱିଜେଟ"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"ଏକ ସର୍ଟକଟ ଭାବେ ଲକ ସ୍କ୍ରିନରେ ୱିଜାଟ ଯୋଗ କରିବାକୁ, ସୁନିଶ୍ଚିତ କରନ୍ତୁ ଯେ ଏହା ସେଟିଂସରେ ସକ୍ଷମ ହୋଇଛି।"</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ୟୁଜର୍ ବଦଳାନ୍ତୁ"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ପୁଲଡାଉନ ମେନୁ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ଏହି ସେସନର ସମସ୍ତ ଆପ୍ ଓ ଡାଟା ଡିଲିଟ୍ ହୋଇଯିବ।"</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ସମସ୍ତ ନୀରବ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ଖାଲି କରନ୍ତୁ"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ବିକଳ୍ପ ଦ୍ୱାରା ବିଜ୍ଞପ୍ତି ପଜ୍ ହୋଇଛି"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{କୌଣସି ବିଜ୍ଞପ୍ତି ନାହିଁ}=1{{mode} ଦ୍ୱାରା ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ବିରତ କରାଯାଇଛି}=2{{mode} ଏବଂ ଅନ୍ୟ ଏକ ମୋଡ ଦ୍ୱାରା ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ବିରତ କରାଯାଇଛି}other{{mode} ଏବଂ ଅନ୍ୟ # ମୋଡ ଦ୍ୱାରା ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ବିରତ କରାଯାଇଛି}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"ବର୍ତ୍ତମାନ ଆରମ୍ଭ କରନ୍ତୁ"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ହେଡ ଟ୍ରାକିଂ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ରିଙ୍ଗର୍ ମୋଡ୍ ବଦଳାଇବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ରିଂଗର ମୋଡ"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ମ୍ୟୁଟ"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ଅନ୍-ମ୍ୟୁଟ୍ କରନ୍ତୁ"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ଭାଇବ୍ରେଟ୍"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବା ସମୟରେ ବାମପଟର ବା ଉପରର ଆପକୁ ସୁଇଚ କରନ୍ତୁ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ସମୟରେ: କୌଣସି ଆପକୁ ଗୋଟିଏରୁ ଅନ୍ୟ ଏକ ଆପରେ ବଦଳାନ୍ତୁ"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"ସକ୍ରିୟ ୱିଣ୍ଡୋକୁ ଡିସପ୍ଲେଗୁଡ଼ିକ ମଧ୍ୟରେ ମୁଭ କରନ୍ତୁ"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"ୱିଣ୍ଡୋକୁ ବାମକୁ ମୁଭ କରନ୍ତୁ"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"ୱିଣ୍ଡୋକୁ ଡାହାଣକୁ ମୁଭ କରନ୍ତୁ"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"ୱିଣ୍ଡୋକୁ ବଡ଼ କରନ୍ତୁ"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"ୱିଣ୍ଡୋକୁ ଛୋଟ କରନ୍ତୁ"</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>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"ସିଷ୍ଟମ ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"ସିଷ୍ଟମ ଆପ୍ସ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ମଲ୍ଟିଟାସ୍କିଂ"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"ବର୍ତ୍ତମାନର ଆପ୍ସ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ଇନପୁଟ"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ଆପ ସର୍ଟକଟ"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"କୀବୋର୍ଡ ସର୍ଟକଟ"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"କୀବୋର୍ଡ ସର୍ଟକଟଗୁଡ଼ିକୁ କଷ୍ଟମାଇଜ କରନ୍ତୁ"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ସର୍ଟକଟକୁ କାଢ଼ି ଦେବେ?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ସର୍ଟକଟ ଆସାଇନ କରିବା ପାଇଁ କୀ\'କୁ ଦବାନ୍ତୁ"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ଏହା ଆପଣଙ୍କ କଷ୍ଟମ ସର୍ଟକଟକୁ ସ୍ଥାୟୀ ଭାବେ ଡିଲିଟ କରିଦେବ।"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ସର୍ଚ୍ଚ ସର୍ଟକଟ"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"କୌଣସି ସର୍ଚ୍ଚ ଫଳାଫଳ ନାହିଁ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ଆଇକନକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ଆକ୍ସନ କିମ୍ବା ମେଟା କୀ ଆଇକନ"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ପ୍ଲସ ଆଇକନ"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"କଷ୍ଟମାଇଜ କରନ୍ତୁ"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ହୋଇଗଲା"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ଆଇକନକୁ ବିସ୍ତାର କରନ୍ତୁ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"କିମ୍ବା"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"ପ୍ଲସ"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ଫରୱାର୍ଡ ସ୍ଲାସ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ଡ୍ରାଗ ହେଣ୍ଡେଲ"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"କୀବୋର୍ଡ ସେଟିଂ"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ସର୍ଟକଟ ସେଟ କରନ୍ତୁ"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"କାଢ଼ି ଦିଅନ୍ତୁ"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ବାତିଲ କରନ୍ତୁ"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"କୀ ଦବାନ୍ତୁ"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"କୀ କମ୍ବିନେସନ ପୂର୍ବରୁ ବ୍ୟବହାର କରାଯାଉଛି। ଅନ୍ୟ ଏକ କୀ ବ୍ୟବହାର କରି ଦେଖନ୍ତୁ।"</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ଆପଣଙ୍କର କୀବୋର୍ଡରେ ଆକ୍ସନ କୀ\'କୁ ଦବାନ୍ତୁ"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ବହୁତ ବଢ଼ିଆ!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"ଆପଣ ସମସ୍ତ ଆପ୍ସ ଜେଶ୍ଚରକୁ ଭ୍ୟୁ କରିବା ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 1dd4079..ba51bf2 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ਵਿਜੇਟ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਐਪ ਖੋਲ੍ਹਣ ਲਈ, ਤੁਹਾਨੂੰ ਇਹ ਪੁਸ਼ਟੀ ਕਰਨ ਦੀ ਲੋੜ ਪਵੇਗੀ ਕਿ ਇਹ ਤੁਸੀਂ ਹੀ ਹੋ। ਨਾਲ ਹੀ, ਇਹ ਵੀ ਧਿਆਨ ਵਿੱਚ ਰੱਖੋ ਕਿ ਕੋਈ ਵੀ ਉਨ੍ਹਾਂ ਨੂੰ ਦੇਖ ਸਕਦਾ ਹੈ, ਭਾਵੇਂ ਤੁਹਾਡਾ ਟੈਬਲੈੱਟ ਲਾਕ ਹੋਵੇ। ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਕੁਝ ਵਿਜੇਟ ਤੁਹਾਡੀ ਲਾਕ ਸਕ੍ਰੀਨ ਲਈ ਨਾ ਬਣੇ ਹੋਣ ਅਤੇ ਉਨ੍ਹਾਂ ਨੂੰ ਇੱਥੇ ਸ਼ਾਮਲ ਕਰਨਾ ਅਸੁਰੱਖਿਅਤ ਹੋਵੇ।"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ਸਮਝ ਲਿਆ"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ਵਿਜੇਟ"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਵਿਜੇਟ ਨੂੰ ਸ਼ਾਰਟਕੱਟ ਵਜੋਂ ਸ਼ਾਮਲ ਕਰਨ ਲਈ, ਪੱਕਾ ਕਰੋ ਕਿ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਇਹ ਸੁਵਿਧਾ ਚਾਲੂ ਹੈ।"</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ਵਰਤੋਂਕਾਰ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ਪੁੱਲਡਾਊਨ ਮੀਨੂ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ਇਸ ਸੈਸ਼ਨ ਵਿਚਲੀਆਂ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟੇ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ਸੂਚਨਾਵਾਂ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"ਗੱਲਾਂਬਾਤਾਂ"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ਸਾਰੀਆਂ ਸ਼ਾਂਤ ਸੂਚਨਾਵਾਂ ਕਲੀਅਰ ਕਰੋ"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਵੱਲੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਰੋਕਿਆ ਗਿਆ"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{ਕੋਈ ਸੂਚਨਾ ਨਹੀਂ ਹੈ}=1{{mode} ਵੱਲੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ}=2{{mode} ਅਤੇ ਇੱਕ ਹੋਰ ਮੋਡ ਵੱਲੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ}other{{mode} ਅਤੇ # ਹੋਰ ਮੋਡਾਂ ਵੱਲੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"ਹੁਣੇ ਸ਼ੁਰੂ ਕਰੋ"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ਹੈੱਡ ਟਰੈਕਿੰਗ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ਰਿੰਗਰ ਮੋਡ ਨੂੰ ਬਦਲਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ਰਿੰਗਰ ਮੋਡ"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ਮਿਊਟ ਕਰੋ"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ਅਣਮਿਊਟ ਕਰੋ"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ਥਰਥਰਾਹਟ"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰਨ ਵੇਲੇ ਖੱਬੇ ਜਾਂ ਉੱਪਰ ਮੌਜੂਦ ਐਪ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੌਰਾਨ: ਇੱਕ ਐਪ ਨਾਲ ਦੂਜੀ ਐਪ ਨੂੰ ਬਦਲੋ"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"ਕਿਰਿਆਸ਼ੀਲ ਵਿੰਡੋ ਨੂੰ ਇੱਕ ਤੋਂ ਦੂਜੇ ਡਿਸਪਲੇ \'ਤੇ ਲਿਜਾਓ"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"ਵਿੰਡੋ ਨੂੰ ਖੱਬੇ ਪਾਸੇ ਲਿਜਾਓ"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"ਵਿੰਡੋ ਨੂੰ ਸੱਜੇ ਪਾਸੇ ਲਿਜਾਓ"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"ਵਿੰਡੋ ਨੂੰ ਵੱਡਾ ਕਰੋ"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"ਵਿੰਡੋ ਨੂੰ ਛੋਟਾ ਕਰੋ"</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>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"ਸਿਸਟਮ ਸੰਬੰਧੀ ਕੰਟਰੋਲ"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"ਸਿਸਟਮ ਐਪਾਂ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ਮਲਟੀਟਾਸਕਿੰਗ"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"ਹਾਲੀਆ ਐਪਾਂ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ਇਨਪੁੱਟ"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ਐਪ ਸ਼ਾਰਟਕੱਟ"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ ਵਿਉਂਤਬੱਧ ਕਰੋ"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ਕੀ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਹਟਾਉਣਾ ਹੈ?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ਸ਼ਾਰਟਕੱਟ ਨਿਰਧਾਰਿਤ ਕਰਨ ਲਈ ਕੁੰਜੀ ਦਬਾਓ"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ਇਸ ਨਾਲ ਤੁਹਾਡੇ ਵਿਉਂਤੇ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਪੱਕੇ ਤੌਰ \'ਤੇ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ਸ਼ਾਰਟਕੱਟ ਖੋਜੋ"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ਕੋਈ ਖੋਜ ਨਤੀਜਾ ਨਹੀਂ ਮਿਲਿਆ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ਪ੍ਰਤੀਕ ਨੂੰ ਸਮੇਟੋ"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ਕਾਰਵਾਈ ਜਾਂ Meta ਕੁੰਜੀ ਪ੍ਰਤੀਕ"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ਜੋੜ-ਚਿੰਨ੍ਹ ਦਾ ਪ੍ਰਤੀਕ"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ਵਿਉਂਤਬੱਧ ਕਰੋ"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ਹੋ ਗਿਆ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ਪ੍ਰਤੀਕ ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ਜਾਂ"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"ਪਲੱਸ"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ਫਾਰਵਰਡ ਸਲੈਸ਼"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ਘਸੀਟਣ ਵਾਲਾ ਹੈਂਡਲ"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ਕੀ-ਬੋਰਡ ਸੈਟਿੰਗਾਂ"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ਸ਼ਾਰਟਕੱਟ ਸੈੱਟ ਕਰੋ"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ਹਟਾਓ"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ਰੱਦ ਕਰੋ"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ਕੁੰਜੀ ਦਬਾਓ"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"ਕੁੰਜੀ ਸੁਮੇਲ ਪਹਿਲਾਂ ਹੀ ਵਰਤੋਂ ਵਿੱਚ ਹੈ। ਕੋਈ ਹੋਰ ਕੁੰਜੀ ਵਰਤ ਕੇ ਦੇਖੋ।"</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ਆਪਣੇ ਕੀ-ਬੋਰਡ \'ਤੇ ਕਾਰਵਾਈ ਕੁੰਜੀ ਨੂੰ ਦਬਾਓ"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ਬਹੁਤ ਵਧੀਆ!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"ਤੁਸੀਂ \'ਸਾਰੀਆਂ ਐਪਾਂ ਦੇਖੋ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ ਹੈ"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 82fe861..6785668 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Aby otworzyć aplikację za pomocą widżetu, musisz potwierdzić swoją tożsamość. Pamiętaj też, że każdy będzie mógł wyświetlić widżety nawet wtedy, gdy tablet będzie zablokowany. Niektóre widżety mogą nie być przeznaczone do umieszczenia na ekranie blokady i ich dodanie w tym miejscu może być niebezpieczne."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widżety"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Aby dodać Widżety na ekranie blokady jako skrót, upewnij się, że ta opcja jest włączona w ustawieniach."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Aby dodać skrót „Widżety”, upewnij się, że opcja „Pokaż widżety na ekranie blokady” jest włączona w ustawieniach."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Ustawienia"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Przełącz użytkownika"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wszystkie aplikacje i dane w tej sesji zostaną usunięte."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Powiadomienia"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Rozmowy"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Usuń wszystkie ciche powiadomienia"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Powiadomienia wstrzymane przez tryb Nie przeszkadzać"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Brak powiadomień}=1{Powiadomienia są wstrzymane przez tryb {mode}}=2{Powiadomienia są wstrzymane przez tryb {mode} i 1 inny tryb}few{Powiadomienia są wstrzymane przez tryb {mode} i # inne tryby}many{Powiadomienia są wstrzymane przez tryb {mode} i # innych trybów}other{Powiadomienia są wstrzymane przez tryb {mode} i # innego trybu}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Rozpocznij teraz"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Śledzenie głowy"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Kliknij, aby zmienić tryb dzwonka"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"tryb dzwonka"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"wycisz"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"wyłącz wyciszenie"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"włącz wibracje"</string>
@@ -875,14 +880,10 @@
<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="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Przenieś aktywne okno na inny ekran"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Przenieś okno w lewo"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Przenieś okno w prawo"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maksymalizuj okno"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimalizuj okno"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Wprowadzanie"</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>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Systemowe elementy sterujące"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplikacje systemowe"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Wielozadaniowość"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Ostatnie aplikacje"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Podzielony ekran"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Wprowadzanie"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Skróty do aplikacji"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Skróty klawiszowe"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Dostosuj skróty klawiszowe"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Usunąć skrót?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Zresetować do ustawień domyślnych?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Naciśnij klawisz, aby przypisać skrót"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Spowoduje to trwałe usunięcie skrótu niestandardowego."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Spowoduje to trwałe usunięcie wszystkich skrótów niestandardowych."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Skróty do wyszukiwania"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Brak wyników wyszukiwania"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zwijania"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona klawisza działania/meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona plusa"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Dostosuj"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotowe"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozwijania"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"lub"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ukośnik prawy"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Uchwyt do przeciągania"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ustawienia klawiatury"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ustaw skrót"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Usuń"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Tak, zresetuj"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Anuluj"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Naciśnij klawisz"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinacja klawiszy jest już używana. Użyj innego klawisza."</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Naciśnij klawisz działania na klawiaturze"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Brawo!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Znasz już gest wyświetlania wszystkich aplikacji"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podświetlenie klawiatury"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Poziom %1$d z %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Sterowanie domem"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index e65dccb..9515227 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -306,7 +306,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Compartilhamento de áudio"</string>
- <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="8680997711431098238">"Compatível com compartilhamento de áudio"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="8680997711431098238">"Aceita compartilhamento de áudio"</string>
<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>
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir um app usando um widget, você precisa confirmar sua identidade. E não se esqueça que qualquer pessoa pode ver os widgets, mesmo com o tablet bloqueado. Além disso, alguns apps não foram criados para a tela de bloqueio, é melhor manter a segurança."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendi"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Para adicionar o recurso \"Widgets na tela de bloqueio\" como um atalho, verifique se os atalhos estão ativados nas configurações."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificações"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversas"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Apagar todas as notificações silenciosas"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificações pausadas pelo modo \"Não perturbe\""</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Sem notificações}=1{Notificações pausadas pelo modo {mode}}=2{Notificações pausadas por {mode} e mais um modo}one{Notificações pausadas por {mode} e mais # modo}many{Notificações pausadas por {mode} e mais # de modos}other{Notificações pausadas por {mode} e mais # modos}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Iniciar agora"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Rastreamento de cabeça"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toque para mudar o modo da campainha"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"modo da campainha"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desativar o som"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ativar o som"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Mudar para o app à esquerda ou acima ao usar a tela dividida"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Com a tela dividida: substituir um app por outro"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Mover janela ativa entre telas"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Mover janela para a esquerda"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Mover janela para a direita"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximizar janela"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimizar janela"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Mudar para o próximo idioma"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Mudar para o idioma anterior"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Controles do sistema"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Apps do sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarefas"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Apps recentes"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Tela dividida"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atalhos de apps"</string>
@@ -1428,25 +1430,30 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos do teclado"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizar atalhos de teclado"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remover atalho?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Pressione a tecla para atribuir o atalho"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Essa ação vai excluir permanentemente seu atalho personalizado."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pesquisar atalhos"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nenhum resultado de pesquisa"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ícone da tecla de ação"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ícone de adição"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Redefinir"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Concluir"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"mais"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"barra para a direita"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Alça de arrastar"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configurações do teclado"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Definir atalho"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Remover"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pressione a tecla"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Essa combinação de teclas já está em uso. Tente outra tecla."</string>
@@ -1478,6 +1485,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pressione a tecla de ação no teclado"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Muito bem!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Você concluiu o gesto para ver todos os apps"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 3dcba30..40df4c0 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir uma app através de um widget, vai ter de validar a sua identidade. Além disso, tenha em atenção que qualquer pessoa pode ver os widgets, mesmo quando o tablet estiver bloqueado. Alguns widgets podem não se destinar ao ecrã de bloqueio e pode ser inseguro adicioná-los aqui."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Para adicionar widgets ao ecrã de bloqueio como um atalho, certifique-se de que estão ativados nas definições."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para adicionar o atalho \"Widgets\", certifique-se de que a opção \"Mostrar widgets no ecrã de bloqueio\" está ativada nas definições."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Definições"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mudar utilizador"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu pendente"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todas as apps e dados desta sessão serão eliminados."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificações"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversas"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Limpar todas as notificações silenciosas"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificações colocadas em pausa pelo modo Não incomodar."</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Sem notificações}=1{Notificações pausadas pelo modo {mode}}=2{Notificações pausadas pelo modo {mode} e mais um modo}many{Notificações pausadas pelo modo {mode} e mais # modos}other{Notificações pausadas pelo modo {mode} e mais # modos}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Começar agora"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Posição da cabeça"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toque para alterar o modo de campainha"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"modo de som"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, toque para alterar o modo de som"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desativar som"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"reativar som"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -1415,7 +1419,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Controlos do sistema"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Apps do sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Execução de várias tarefas em simultâneo"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Apps recentes"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ecrã dividido"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atalhos de apps"</string>
@@ -1424,14 +1427,17 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos de teclado"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalize os atalhos de teclado"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remover atalho?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Repor para a predefinição?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Prima a tecla para atribuir o atalho"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Esta ação elimina o atalho personalizado permanentemente."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Esta ação elimina todos os seus atalhos personalizados permanentemente."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pesquisar atalhos"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nenhum resultado da pesquisa"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone de reduzir"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ícone da tecla Meta ou de ação"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ícone de mais"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Repor"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Concluir"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone de expandir"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
@@ -1441,6 +1447,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Definições do teclado"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Configurar atalho"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Remover"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sim, repor"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Prima a tecla"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"A combinação de teclas já está a ser usada. Experimente outra tecla."</string>
@@ -1472,6 +1479,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Prima a tecla de ação no teclado"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Muito bem!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Concluiu o gesto para ver todas as apps"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Animação do tutorial, clique para pausar e retomar a reprodução."</string>
<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>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index e65dccb..9515227 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -306,7 +306,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Compartilhamento de áudio"</string>
- <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="8680997711431098238">"Compatível com compartilhamento de áudio"</string>
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="8680997711431098238">"Aceita compartilhamento de áudio"</string>
<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>
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir um app usando um widget, você precisa confirmar sua identidade. E não se esqueça que qualquer pessoa pode ver os widgets, mesmo com o tablet bloqueado. Além disso, alguns apps não foram criados para a tela de bloqueio, é melhor manter a segurança."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendi"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgets"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Para adicionar o recurso \"Widgets na tela de bloqueio\" como um atalho, verifique se os atalhos estão ativados nas configurações."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificações"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversas"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Apagar todas as notificações silenciosas"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificações pausadas pelo modo \"Não perturbe\""</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Sem notificações}=1{Notificações pausadas pelo modo {mode}}=2{Notificações pausadas por {mode} e mais um modo}one{Notificações pausadas por {mode} e mais # modo}many{Notificações pausadas por {mode} e mais # de modos}other{Notificações pausadas por {mode} e mais # modos}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Iniciar agora"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Rastreamento de cabeça"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toque para mudar o modo da campainha"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"modo da campainha"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desativar o som"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ativar o som"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Mudar para o app à esquerda ou acima ao usar a tela dividida"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Com a tela dividida: substituir um app por outro"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Mover janela ativa entre telas"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Mover janela para a esquerda"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Mover janela para a direita"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximizar janela"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimizar janela"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Mudar para o próximo idioma"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Mudar para o idioma anterior"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Controles do sistema"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Apps do sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarefas"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Apps recentes"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Tela dividida"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atalhos de apps"</string>
@@ -1428,25 +1430,30 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos do teclado"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizar atalhos de teclado"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remover atalho?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Pressione a tecla para atribuir o atalho"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Essa ação vai excluir permanentemente seu atalho personalizado."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pesquisar atalhos"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nenhum resultado de pesquisa"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ícone da tecla de ação"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ícone de adição"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Redefinir"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Concluir"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"mais"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"barra para a direita"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Alça de arrastar"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configurações do teclado"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Definir atalho"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Remover"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pressione a tecla"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Essa combinação de teclas já está em uso. Tente outra tecla."</string>
@@ -1478,6 +1485,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pressione a tecla de ação no teclado"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Muito bem!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Você concluiu o gesto para ver todos os apps"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 93333c0..3b5cd89 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pentru a deschide o aplicație folosind un widget, va trebui să-ți confirmi identitatea. În plus, reține că oricine poate să vadă widgeturile, chiar dacă tableta este blocată. Este posibil ca unele widgeturi să nu fi fost create pentru ecranul de blocare și poate fi nesigur să le adaugi aici."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgeturi"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Pentru a adăuga widgeturi pe ecranul de blocare drept comandă rapidă, verifică dacă sunt activate în setări."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Schimbă utilizatorul"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"meniu vertical"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toate aplicațiile și datele din această sesiune vor fi șterse."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificări"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversații"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Șterge toate notificările silențioase"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificări întrerupte prin „Nu deranja”"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Nicio notificare}=1{Notificările au fost întrerupte de {mode}}=2{Notificările au fost întrerupte de {mode} și de un alt mod}few{Notificările au fost întrerupte de {mode} și de alte # moduri}other{Notificările au fost întrerupte de {mode} și de alte # de moduri}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Începe acum"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Urmărirea mișcărilor capului"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Atinge pentru a schimba modul soneriei"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"modul sonerie"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"dezactivează sunetul"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"activează sunetul"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrații"</string>
@@ -875,14 +882,10 @@
<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>
<string name="system_multitasking_replace" msgid="7410071959803642125">"În modul ecran împărțit: înlocuiește o aplicație cu alta"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Mută fereastra activă de pe un ecran pe altul"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Mută fereastra spre stânga"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Mută fereastra spre dreapta"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximizează fereastra"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimizează fereastra"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Introducere"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Comută la următoarea limbă"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Comută la limba anterioară"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Comenzile sistemului"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplicații de sistem"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Aplicații recente"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ecran împărțit"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Intrare"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Comenzi rapide pentru aplicații"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Comenzi rapide de la tastatură"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizează comenzile rapide de la tastatură"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Elimini comanda rapidă?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Apasă tasta pentru a atribui comanda rapidă"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Astfel, se va șterge definitiv comanda rapidă personalizată."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Comenzi directe de căutare"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Niciun rezultat al căutării"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Pictograma de restrângere"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Pictograma pentru acțiune sau tastă Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Pictograma plus"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizează"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gata"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Pictograma de extindere"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"sau"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"bară oblică spre dreapta"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ghidaj de tragere"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Setările tastaturii"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Setează o comandă rapidă"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Elimină"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Anulează"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Apasă tasta"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Combinația de taste este deja folosită. Încearcă altă tastă."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Apasă tasta de acțiuni de pe tastatură"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Felicitări!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Ai finalizat gestul pentru afișarea tuturor aplicațiilor"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Iluminarea din spate a tastaturii"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivelul %1$d din %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Comenzi pentru locuință"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 4641e01..b026787 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Чтобы открыть приложение, используя виджет, вам нужно будет подтвердить свою личность. Обратите внимание, что виджеты видны всем, даже если планшет заблокирован. Некоторые виджеты не предназначены для использования на заблокированном экране. Добавлять их туда может быть небезопасно."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ОК"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виджеты"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Чтобы добавить виджеты на заблокированный экран, включите в настройках параметр \"Виджеты на заблокированном экране\"."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Чтобы создать ярлык \"Виджеты\", убедитесь, что в настройках включена функция \"Показывать виджеты на заблокированном экране\"."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Настройки"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Сменить пользователя."</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"раскрывающееся меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Все приложения и данные этого профиля будут удалены."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Уведомления"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Разговоры"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Отклонить все беззвучные уведомления"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"В режиме \"Не беспокоить\" уведомления заблокированы"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Уведомлений нет}=1{Режим \"{mode}\" приостанавливает уведомления}=2{Режим \"{mode}\" и ещё один режим приостанавливают уведомления}one{Режим \"{mode}\" и ещё # режим приостанавливают уведомления}few{Режим \"{mode}\" и ещё # режима приостанавливают уведомления}many{Режим \"{mode}\" и ещё # режимов приостанавливают уведомления}other{Режим \"{mode}\" и ещё # режима приостанавливают уведомления}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Начать"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Динамичное"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Нажмите, чтобы изменить режим звонка."</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"режим звонка"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"отключить звук"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"включить звук"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"включить вибрацию"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Перейти к приложению слева или вверху на разделенном экране"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"В режиме разделения экрана заменить одно приложение другим"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Переместить активное окно между экранами"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Переместить окно влево"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Переместить окно вправо"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Развернуть окно"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Свернуть окно"</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>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Управление системой"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системные приложения"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Многозадачность"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Недавние приложения"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Разделение экрана"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ввод"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Быстрые клавиши для приложений"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Быстрые клавиши"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Как настроить быстрые клавиши"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Удалить сочетание клавиш?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Сбросить настройки?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Нажмите клавишу, чтобы назначить сочетание клавиш."</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Настроенное сочетание будет безвозвратно удалено."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Все пользовательские ярлыки будут безвозвратно удалены."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Найти быстрые клавиши"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ничего не найдено"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Свернуть\""</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Значок клавиши Meta для выполнения действия"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Значок плюса"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Настроить"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Развернуть\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"плюс"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"косая черта"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер перемещения"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Настройки клавиатуры"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Задать сочетание клавиш"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Удалить"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Сбросить"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Отмена"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Нажмите клавишу"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Это сочетание клавиш уже используется. Попробуйте другое."</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Нажмите клавишу действия на клавиатуре."</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Блестяще!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Вы выполнили жест для просмотра всех приложений."</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 7f85b6f..a496c7c 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"විජට් එකක් භාවිතයෙන් යෙදුමක් විවෘත කිරීමට, ඔබට ඒ ඔබ බව සත්යාපනය කිරීමට අවශ්ය වනු ඇත. එසේම, ඔබේ ටැබ්ලටය අගුළු දමා ඇති විට පවා ඕනෑම කෙනෙකුට ඒවා බැලිය හැකි බව මතක තබා ගන්න. සමහර විජට් ඔබේ අගුළු තිරය සඳහා අදහස් කර නොතිබිය හැකි අතර මෙහි එක් කිරීමට අනාරක්ෂිත විය හැක."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"තේරුණා"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"විජට්"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"කෙටිමඟක් ලෙස අගුළු තිරය මත Widgets එක් කිරීමට, එය සැකසීම් තුළ සබල කර ඇති බවට වග බලා ගන්න."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"පරිශීලක මාරුව"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"නිපතන මෙනුව"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"මෙම සැසියේ සියළුම යෙදුම් සහ දත්ත මකාවී."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"දැනුම් දීම්"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"සංවාද"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"සියලු නිහඬ දැනුම්දීම් හිස් කරන්න"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"බාධා නොකරන්න මගින් විරාම කරන ලද දැනුම්දීම්"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{දැනුම්දීම් නැත}=1{{mode} මගින් දැනුම්දීම් විරාම කරන ලදි}=2{{mode} සහ තව එක ප්රකාරයක් මගින් දැනුම්දීම් විරාම කරන ලදි}one{{mode} සහ තව ප්රකාර #ක් මගින් දැනුම්දීම් විරාම කරන ලදි}other{{mode} සහ තව ප්රකාර #ක් මගින් දැනුම්දීම් විරාම කරන ලදි}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"දැන් අරඹන්න"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"හිස ලුහුබැඳීම"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"නාදකය වෙනස් කිරීමට තට්ටු කරන්න"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"හඬ නඟන ආකාරය"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"නිහඬ කරන්න"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"නිශ්ශබ්දතාවය ඉවත් කරන්න"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"කම්පනය"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"බෙදුම් තිරය භාවිත කරන අතරතුර වමේ හෝ ඉහළ ඇති යෙදුමට මාරු වන්න"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"බෙදුම් තිරය අතරතුර: යෙදුමක් එකකින් තවත් එකක් ප්රතිස්ථාපනය කරන්න"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"සක්රිය කවුළුව සංදර්ශක අතර ගෙන යන්න"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"කවුළුව වමට ගෙන යන්න"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"කවුළුව දකුණට ගෙන යන්න"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"කවුළුව විහිදන්න"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"කවුළුව කුඩා කරන්න"</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>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"පද්ධති පාලන"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"පද්ධති යෙදුම්"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"බහුකාර්ය"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"මෑත යෙදුම්"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"බෙදුම් තිරය"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ආදානය"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"යෙදුම් කෙටිමං"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"යතුරු පුවරු කෙටි මං"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"යතුරුපුවරු කෙටිමං අභිරුචිකරණය කරන්න"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"කෙටිමඟ ඉවත් කරන්න ද?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"කෙටිමඟ පැවරීමට යතුර ඔබන්න"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"මෙය ඔබේ අභිරුචි කෙටිමඟ ස්ථිරවම මකනු ඇත."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"කෙටි මං සොයන්න"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"සෙවීම් ප්රතිඵල නැත"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"හැකුළුම් නිරූපකය"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ක්රියාව හෝ Meta යතුරු නිරූපකය"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ධන නිරූපකය"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"අභිරුචිකරණය කරන්න"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"නිමයි"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"දිගහැරීම් නිරූපකය"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"හෝ"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"ධන"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ඉදිරියට ඉර"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ඇදීම් හැඬලය"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"යතුරු පුවරු සැකසීම්"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"කෙටිමඟ සකසන්න"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ඉවත් කරන්න"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"අවලංගු කරන්න"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"යතුර ඔබන්න"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"යතුරු සංයෝජනය දැනටමත් භාවිත වේ. වෙනත් යතුරක් උත්සාහ කරන්න."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ඔබේ යතුරු පුවරුවේ ක්රියාකාරී යතුර ඔබන්න"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"හොඳින් කළා!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"ඔබ සියලු යෙදුම් ඉංගිත බැලීම සම්පූර්ණ කර ඇත"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 6dc7a66..0c4880d 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ak chcete otvoriť aplikáciu pomocou miniaplikácie, budete musieť overiť svoju totožnosť. Pamätajte, že si miniaplikáciu môže pozrieť ktokoľvek, aj keď máte tablet uzamknutý. Niektoré miniaplikácie možno nie sú určené pre uzamknutú obrazovku a ich pridanie tu môže byť nebezpečné."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Dobre"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Miniaplikácie"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Ak chcete na uzamknutú obrazovku pridať miniaplikácie ako odkaz, uistite sa, že sú v nastaveniach povolené."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Ak chcete pridať odkaz Miniaplikácie, uistite sa, že v nastaveniach je zapnutá možnosť Zobrazovať miniaplikácie na uzamknutej obrazovke."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Nastavenia"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Prepnutie používateľa"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rozbaľovacia ponuka"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Všetky aplikácie a údaje v tejto relácii budú odstránené."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Upozornenia"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Konverzácie"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Vymazať všetky tiché upozornenia"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Upozornenia sú pozastavené režimom bez vyrušení"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Žiadne upozornenia}=1{Upozornenia boli pozastavené režimom {mode}}=2{Upozornenia boli pozastavené režimom {mode} a jedným ďalším}few{Upozornenia boli pozastavené režimom {mode} a # ďalšími}many{Notifications paused by {mode} and # other modes}other{Upozornenia boli pozastavené režimom {mode} a # ďalšími}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Spustiť"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Sledovanie hlavy"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Režim zvonenia zmeníte klepnutím"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"režim zvonenia"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vypnite zvuk"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"zapnite zvuk"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"zapnite vibrovanie"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Prechod na aplikáciu vľavo alebo hore pri rozdelenej obrazovke"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Počas rozdelenej obrazovky: nahradenie aplikácie inou"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Presun aktívneho okna medzi obrazovkami"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Presun okna doľava"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Presun okna doprava"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximalizovanie okna"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimalizovanie okna"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Vstup"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Prepnutie na ďalší jazyk"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Prepnutie na predchádzajúci jazyk"</string>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Ovládanie systému"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systémové aplikácie"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Nedávne aplikácie"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Rozdelená obrazovka"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Vstup"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Skratky aplikácií"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové skratky"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prispôsobenie klávesových skratiek"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Chcete skratku odstrániť?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Chcete resetovať na predvolené nastavenie?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Stlačením klávesa priraďte skratku"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Týmto natrvalo odstránite vlastnú skratku."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Týmto natrvalo odstránite všetky vlastné odkazy."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prehľadávať skratky"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Žiadne výsledky vyhľadávania"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zbalenia"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona akčného klávesa alebo metaklávesa"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona plus"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prispôsobiť"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Hotovo"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalenia"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"alebo"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"lomka"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Presúvadlo"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Nastavenia klávesnice"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Nastaviť skratku"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Odstrániť"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Áno, resetovať"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Zrušiť"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Stlačte kláves"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinácia klávesov sa už používa. Skúste iný kláves."</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Stlačte na klávesnici akčný kláves"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Dobre!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Použili ste gesto na zobrazenie všetkých aplikácií."</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvietenie klávesnice"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. úroveň z %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládanie domácnosti"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 74fbd06..dbeed9d 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Če želite aplikacijo odpreti s pripomočkom, morate potrditi, da ste to vi. Upoštevajte tudi, da si jih lahko ogledajo vsi, tudi ko je tablični računalnik zaklenjen. Nekateri pripomočki morda niso predvideni za uporabo na zaklenjenem zaslonu, zato jih tukaj morda ni varno dodati."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Razumem"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Pripomočki"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Če želite na zaklenjen zaslon dodati pripomočke kot bližnjico, morate to omogočiti v nastavitvah."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Če želite dodati bližnjico »Pripomočki«, v nastavitvah omogočite možnost »Prikaz pripomočkov na zaklenjenem zaslonu«."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Nastavitve"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Preklop med uporabniki"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"spustni meni"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Vse aplikacije in podatki v tej seji bodo izbrisani."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Obvestila"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Pogovori"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Brisanje vseh tihih obvestil"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Prikazovanje obvestil je začasno zaustavljeno z načinom »ne moti«"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Ni obvestil}=1{Prikazovanje obvestil je začasno zaustavljeno z načinom {mode}}=2{Prikazovanje obvestil je začasno zaustavljeno z načinom {mode} in še enim drugim načinom}one{Prikazovanje obvestil je začasno zaustavljeno z načinom {mode} in še # drugim načinom}two{Prikazovanje obvestil je začasno zaustavljeno z načinom {mode} in še # drugima načinoma}few{Prikazovanje obvestil je začasno zaustavljeno z načinom {mode} in še # drugimi načini}other{Prikazovanje obvestil je začasno zaustavljeno z načinom {mode} in še # drugimi načini}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Začni zdaj"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Spremljanje glave"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Dotaknite se, če želite spremeniti način zvonjenja."</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"način zvonjenja"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"izklop zvoka"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"vklop zvoka"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string>
@@ -1415,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Sistemski kontrolniki"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistemske aplikacije"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Večopravilnost"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Nedavne aplikacije"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Razdeljen zaslon"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Vnos"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Bližnjice do aplikacij"</string>
@@ -1424,14 +1428,17 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Bližnjične tipke"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prilagajanje bližnjičnih tipk"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Želite odstraniti bližnjico?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Želite ponastaviti na privzete bližnjice?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Pritisnite tipko za dodelitev bližnjice"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"S tem boste trajno izbrisali bližnjico po meri."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"S tem boste trajno izbrisali vse bližnjice po meri."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Iskanje po bližnjicah"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ni rezultatov iskanja"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za strnitev"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona tipke za dejanje ali metapodatke"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona znaka plus"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagodi"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Ponastavi"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Končano"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za razširitev"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ali"</string>
@@ -1441,6 +1448,7 @@
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Nastavitve tipkovnice"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Nastavite bližnjico"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Odstrani"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Da, ponastavi"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Prekliči"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pritisnite tipko"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinacija tipk je že v uporabi. Poskusite z drugo tipko."</string>
@@ -1472,6 +1480,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite tipko za dejanja na tipkovnici"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Odlično!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Izvedli ste potezo za ogled vseh aplikacij"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index bd68061..743aad0 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -306,8 +306,7 @@
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Përdor Bluetooth-in"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Lidhur"</string>
<string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Ndarja e audios"</string>
- <!-- no translation found for quick_settings_bluetooth_device_audio_sharing_or_switch_active (8680997711431098238) -->
- <skip />
+ <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="8680997711431098238">"Mbështet ndarjen e audios"</string>
<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>
@@ -530,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Për të hapur një aplikacion duke përdorur një miniaplikacion, do të duhet të verifikosh që je ti. Ki parasysh gjithashtu që çdo person mund t\'i shikojë, edhe kur tableti yt është i kyçur. Disa miniaplikacione mund të mos jenë planifikuar për ekranin tënd të kyçjes dhe mund të mos jetë e sigurt t\'i shtosh këtu."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"E kuptova"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Miniaplikacionet"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Për të shtuar \"Miniaplikacionet në ekranin e kyçjes\" si shkurtore, sigurohu që kjo veçori të jetë aktivizuar te cilësimet."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Ndërro përdorues"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menyja me tërheqje poshtë"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Të gjitha aplikacionet dhe të dhënat në këtë sesion do të fshihen."</string>
@@ -591,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Njoftimet"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Bisedat"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Pastro të gjitha njoftimet në heshtje"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Njoftimet janë vendosur në pauzë nga modaliteti \"Mos shqetëso\""</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Asnjë njoftim}=1{Njoftimet u vendosën në pauzë nga {mode}}=2{Njoftimet u vendosën në pauzë nga {mode} dhe një modalitet tjetër}other{Njoftimet u vendosën në pauzë nga {mode} dhe # modalitete të tjera}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Fillo tani"</string>
@@ -705,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Ndjekja e lëvizjeve të kokës"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Trokit për të ndryshuar modalitetin e ziles"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"modaliteti i ziles"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"çaktivizo audion"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktivizo audion"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"lësho dridhje"</string>
@@ -876,14 +882,10 @@
<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>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Gjatë ekranit të ndarë: zëvendëso një aplikacion me një tjetër"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Zhvendose dritaren aktive mes ekraneve"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Zhvendos dritaren në të majtë"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Zhvendos dritaren në të djathtë"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maksimizo dritaren"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimizo dritaren"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Hyrja"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Kalo te gjuha tjetër"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Kalo te gjuha e mëparshme"</string>
@@ -1420,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Kontrollet e sistemit"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplikacionet e sistemit"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Kryerja e shumë detyrave"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Aplikacionet e fundit"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ekrani i ndarë"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Hyrja"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Shkurtoret e aplikacionit"</string>
@@ -1429,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Shkurtoret e tastierës"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizo shkurtoret e tastierës"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Të hiqet shkurtorja?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Shtyp tastin për të caktuar shkurtoren"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Kjo do ta fshijë përgjithmonë shkurtoren tënde të personalizuar."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Kërko për shkurtoret"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Asnjë rezultat kërkimi"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona e palosjes"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona e tastit të veprimit ose tastit Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona e plusit"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizo"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"U krye"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona e zgjerimit"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ose"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"vizë e pjerrët djathtas"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Doreza e zvarritjes"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Cilësimet e tastierës"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Cakto shkurtoren"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Hiq"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Anulo"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Shtyp tastin"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinimi i tasteve është tashmë në përdorim. Provo një tast tjetër."</string>
@@ -1479,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Shtyp tastin e veprimit në tastierë"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Shumë mirë!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Përfundove gjestin për shikimin e të gjitha aplikacioneve"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Drita e sfondit e tastierës"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveli: %1$d nga %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrollet e shtëpisë"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index cc18b66..9a15268 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Да бисте отворили апликацију која користи виџет, треба да потврдите да сте то ви. Имајте у виду да свако може да га види, чак и када је таблет закључан. Неки виџети можда нису намењени за закључани екран и можда није безбедно да их тамо додате."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Важи"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Виџети"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Да бисте додали виџете на закључани екран као пречицу, уверите се да је то омогућено у подешавањима."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Да бисте додали пречицу Виџети, уверите се да је у подешавањима омогућено Приказуј виџете на закључаном екрану."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Подешавања"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Замени корисника"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"падајући мени"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Све апликације и подаци у овој сесији ће бити избрисани."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Обавештења"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Конверзације"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Обришите сва нечујна обавештења"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Обавештења су паузирана режимом Не узнемиравај"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Нема обавештења}=1{Обавештења је паузирао {mode}}=2{Обавештења су паузирали {mode} и још један режим}one{Обавештења су паузирали {mode} и још # режим}few{Обавештења су паузирали {mode} и још # режима}other{Обавештења су паузирали {mode} и још # режима}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Започни"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Праћење главе"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Додирните да бисте променили режим звона"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"режим звона"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"искључите звук"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"укључите звук"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибрација"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Пређите у апликацију слева или изнад док користите подељени екран"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"У режиму подељеног екрана: замена једне апликације другом"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Премести активан прозор на следећи екран"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Померите прозор налево"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Померите прозор надесно"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Повећајте прозор"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Смањите прозор"</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>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Системске контроле"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системске апликације"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Обављање више задатака истовремено"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Недавне апликације"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Подељени екран"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Унос"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Пречице за апликације"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Тастерске пречице"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Прилагодите тастерске пречице"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Желите да уклоните пречицу?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Желите да ресетујете на подразумевано?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Притисните тастер да бисте доделили пречицу"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Овим ћете трајно избрисати прилагођену пречицу."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Тиме ћете трајно избрисати све прилагођене пречице."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Претражите пречице"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Нема резултата претраге"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за скупљање"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Икона тастера за радњу или мета тастера"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Икона знака плус"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Прилагоди"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширивање"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"плус"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"коса црта унапред"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер за превлачење"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Подешавања тастатуре"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Подеси пречицу"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Уклони"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Да, ресетуј"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Откажи"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Притисните тастер"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Комбинација тастера се већ користи. Пробајте са другим тастером."</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Притисните тастер радњи на тастатури"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Одлично!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Довршили сте покрет за приказивање свих апликација."</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index fae3c11..c8fee61 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Du måste verifiera din identitet innan du öppnar en app med en widget. Tänk också på att alla kan se dem, även när surfplattan är låst. Vissa widgetar kanske inte är avsedda för låsskärmen och det kan vara osäkert att lägga till dem här."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widgetar"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Om du vill lägga till widgetar på låsskärmen som en genväg måste du se till att de är aktiverade i inställningarna."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Byt användare"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullgardinsmeny"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alla appar och data i denna session kommer att raderas."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Aviseringar"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Konversationer"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Rensa alla ljudlösa aviseringar"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Aviseringar har pausats via Stör ej"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Inga aviseringar}=1{Aviseringar har pausats av {mode}}=2{Aviseringar har pausats av {mode} och ett annat läge}other{Aviseringar har pausats av {mode} och # andra lägen}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Starta nu"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Huvudspårning"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tryck för att ändra ringsignalens läge"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ringsignalläge"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"stänga av ljudet"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå på ljudet"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibration"</string>
@@ -875,14 +882,10 @@
<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>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Med delad skärm: ersätt en app med en annan"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Flytta det aktiva fönstret mellan skärmar"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Flytta fönstret åt vänster"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Flytta fönstret åt höger"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Maximera fönstret"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Minimera fönstret"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Inmatning"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Byt till nästa språk"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Byt till föregående språk"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Systeminställningar"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systemappar"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multikörning"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Senaste apparna"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Delad skärm"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Inmatning"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Genvägar till appar"</string>
@@ -1428,25 +1430,30 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Kortkommandon"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Anpassa kortkommandon"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Vill du ta bort kortkommandot?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Tryck på tangenten för att tilldela ett kortkommando"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Det anpassade kortkommandot raderas permanent."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sökgenvägar"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Inga sökresultat"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikonen Komprimera"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikon för åtgärdstangent"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plusikon"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Anpassa"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Återställ"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Klar"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikonen Utöka"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"snedstreck"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handtag"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tangentbordsinställningar"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ange kortkommando"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Ta bort"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Avbryt"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tryck på tangenten"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tangentkombinationen används redan. Testa en annan tangent."</string>
@@ -1478,6 +1485,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tryck på åtgärdstangenten på tangentbordet"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Bra gjort!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Du är klar med rörelsen för att se alla apparna."</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index d8581e8..44bac92 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Utahitaji kuthibitisha kuwa ni wewe ili ufungue programu ukitumia wijeti. Pia, kumbuka kuwa mtu yeyote anaweza kuziona, hata kishikwambi chako kikiwa kimefungwa. Huenda baadhi ya wijeti hazikukusudiwa kutumika kwenye skrini yako iliyofungwa na huenda si salama kuziweka hapa."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Nimeelewa"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Wijeti"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Ili kuweka kipengele cha Wijeti kwenye skrini iliyofungwa kiwe njia ya mkato, hakikisha kimewashwa katika mipangilio."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Badili mtumiaji"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menyu ya kuvuta chini"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Data na programu zote katika kipindi hiki zitafutwa."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Arifa"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Mazungumzo"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Futa arifa zote zisizo na sauti"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Kipengele cha Usinisumbue kimesitisha arifa"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Hakuna arifa}=1{Arifa zimesitishwa na {mode}}=2{Arifa zimesitishwa na {mode} na hali nyingine moja}other{Arifa zimesitishwa na {mode} na hali nyingine #}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Anza sasa"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Ufuatilizi wa Kichwa"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Gusa ili ubadilishe hali ya programu inayotoa milio ya simu"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"hali ya programu inayotoa milio ya simu"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"zima sauti"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"washa sauti"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"tetema"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Badilisha uende kwenye programu iliyo kushoto au juu unapotumia hali ya kugawa skrini"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Ukigawanya skrini: badilisha kutoka programu moja hadi nyingine"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Hamisha dirisha linalotumika kati ya skrini moja na nyingine"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Sogeza dirisha kushoto"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Sogeza dirisha kulia"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Panua dirisha"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Punguza dirisha"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Vifaa vya kuingiza data"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Badilisha utumie lugha inayofuata"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Badilisha utumie lugha iliyotangulia"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Vidhibiti vya mfumo"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Programu za mfumo"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Majukumu mengi"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Programu za hivi majuzi"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Gawa skrini"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Kifaa cha kuingiza data"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Njia za mikato za programu"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Mikato ya kibodi"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Weka mapendeleo ya mikato ya kibodi"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Ungependa kuondoa njia ya mkato?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Bonyeza kitufe ukabidhi njia ya mkato"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Hatua hii itaondoa kabisa njia yako maalum ya mkato."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Njia mkato za kutafutia"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Hamna matokeo ya utafutaji"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kunja aikoni"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Aikoni ya kitufe cha Vitendo au cha Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Aikoni ya alama ya kujumlisha"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Weka mapendeleo"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Nimemaliza"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Panua aikoni"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"au"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"na"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"mkwaju wa mbele"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Aikoni ya buruta"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Mipangilio ya Kibodi"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Weka njia ya mkato"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Ondoa"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Acha"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Bonyeza kitufe"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tayari unatumia mchanganyiko wa vitufe. Jaribu kitufe kingine."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Bonyeza kitufe cha vitendo kwenye kibodi yako"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Vizuri sana!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Umekamilisha mafunzo ya mguso wa kuangalia programu zote"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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">"Dhibiti Vifaa Nyumbani"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index f2145ff..4c65095 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -117,7 +117,7 @@
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ஓர் ஆப்ஸை ரெக்கார்டு செய்யும்போது அதில் காட்டப்படும் அல்லது பிளே செய்யப்படும் அனைத்தும் ரெக்கார்டு செய்யப்படும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"திரையை ரெக்கார்டு செய்"</string>
<string name="screenrecord_app_selector_title" msgid="3854492366333954736">"ரெக்கார்டு செய்ய ஆப்ஸைத் தேர்வுசெய்தல்"</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>
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"விட்ஜெட்டைப் பயன்படுத்தி ஆப்ஸைத் திறக்க, அது நீங்கள்தான் என்பதை உறுதிசெய்ய வேண்டும். அத்துடன், உங்கள் டேப்லெட் பூட்டப்பட்டிருந்தாலும்கூட அவற்றை யார் வேண்டுமானாலும் பார்க்கலாம் என்பதை நினைவில்கொள்ளுங்கள். சில விட்ஜெட்கள் உங்கள் பூட்டுத் திரைக்காக உருவாக்கப்பட்டவை அல்ல என்பதையும் அவற்றை இங்கே சேர்ப்பது பாதுகாப்பற்றதாக இருக்கக்கூடும் என்பதையும் நினைவில்கொள்ளுங்கள்."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"சரி"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"விட்ஜெட்கள்"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"\'பூட்டுத் திரையில் விட்ஜெட்கள்\' அம்சத்தை ஷார்ட்கட்டாகச் சேர்க்க, அமைப்புகளில் அது இயக்கப்பட்டுள்ளதை உறுதிப்படுத்தவும்."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"பயனரை மாற்று"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"கீழ் இழுக்கும் மெனு"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"இந்த அமர்வின் எல்லா ஆப்ஸும் தரவும் நீக்கப்படும்."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"அறிவிப்புகள்"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"உரையாடல்கள்"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"சைலன்ட் அறிவிப்புகள் அனைத்தையும் அழிக்கும்"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'தொந்தரவு செய்ய வேண்டாம்\' அம்சத்தின் மூலம் அறிவிப்புகள் இடைநிறுத்தப்பட்டுள்ளன"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{அறிவிப்புகள் இல்லை}=1{{mode} பயன்முறையால் அறிவிப்புகள் இடைநிறுத்தப்பட்டுள்ளன}=2{{mode} மற்றும் வேறொரு பயன்முறையால் அறிவிப்புகள் இடைநிறுத்தப்பட்டுள்ளன}other{{mode} மற்றும் வேறு # பயன்முறைகளால் அறிவிப்புகள் இடைநிறுத்தப்பட்டுள்ளன}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"இப்போது தொடங்கு"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ஹெட் டிராக்கிங்"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ரிங்கர் பயன்முறையை மாற்ற தட்டவும்"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ரிங்கர் பயன்முறை"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ஒலியடக்கும்"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ஒலி இயக்கும்"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"அதிர்வுறும்"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"திரைப் பிரிப்பைப் பயன்படுத்தும்போது இடது/மேலே உள்ள ஆப்ஸுக்கு மாறுதல்"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"திரைப் பிரிப்பின்போது: ஓர் ஆப்ஸுக்குப் பதிலாக மற்றொன்றை மாற்றுதல்"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"காட்சிகளுக்கு இடையே செயலில் உள்ள சாளரத்தை நகர்த்துதல்"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"சாளரத்தை இடதுபுறமாக நகர்த்துதல்"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"சாளரத்தை வலதுபுறமாக நகர்த்துதல்"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"சாளரத்தைப் பெரிதாக்குதல்"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"சாளரத்தைச் சிறிதாக்குதல்"</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>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"சிஸ்டம் கட்டுப்பாடுகள்"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"சிஸ்டம் ஆப்ஸ்"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"பல வேலைகளைச் செய்தல்"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"சமீபத்திய ஆப்ஸ்"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"திரைப் பிரிப்பு"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"உள்ளீடு"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ஆப்ஸ் ஷார்ட்கட்கள்"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"கீபோர்டு ஷார்ட்கட்கள்"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"கீபோர்டு ஷார்ட்கட்களைப் பிரத்தியேகப்படுத்துதல்"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ஷார்ட்கட்டை அகற்றவா?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ஷார்ட்கட்டை அமைக்க பட்டனை அழுத்துங்கள்"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"இது உங்கள் பிரத்தியேக ஷார்ட்கட்டை நிரந்தரமாக நீக்கும்."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ஷார்ட்கட்களைத் தேடுக"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"தேடல் முடிவுகள் இல்லை"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"சுருக்குவதற்கான ஐகான்"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ஆக்ஷன்/மெட்டா பட்டன் ஐகான்"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"பிளஸ் ஐகான்"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"பிரத்தியேகப்படுத்தும்"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"முடிந்தது"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"விரிவாக்குவதற்கான ஐகான்"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"அல்லது"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"மற்றும்"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ஃபார்வர்டு ஸ்லாஷ்"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"இழுப்பதற்கான ஹேண்டில்"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"கீபோர்டு அமைப்புகள்"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ஷார்ட்கட்டை அமையுங்கள்"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"அகற்று"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ரத்துசெய்"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"பட்டனை அழுத்துங்கள்"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"பட்டன் சேர்க்கை ஏற்கெனவே பயன்பாட்டில் உள்ளது. வேறொரு பட்டனைப் பயன்படுத்திப் பார்க்கவும்."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"உங்கள் கீபோர்டில் ஆக்ஷன் பட்டனை அழுத்தவும்"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"அருமை!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"அனைத்து ஆப்ஸையும் பார்ப்பதற்கான சைகை பயிற்சியை நிறைவுசெய்துவிட்டீர்கள்"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 2c72796..ebed1877 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"విడ్జెట్ను ఉపయోగించి యాప్ను తెరవడానికి, ఇది మీరేనని వెరిఫై చేయాల్సి ఉంటుంది. అలాగే, మీ టాబ్లెట్ లాక్ చేసి ఉన్నప్పటికీ, ఎవరైనా వాటిని చూడగలరని గుర్తుంచుకోండి. కొన్ని విడ్జెట్లు మీ లాక్ స్క్రీన్కు తగినవి కాకపోవచ్చు, వాటిని ఇక్కడ జోడించడం సురక్షితం కాకపోవచ్చు."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"అర్థమైంది"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"విడ్జెట్లు"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"లాక్ స్క్రీన్లో విడ్జెట్లను షార్ట్కట్గా జోడించడానికి, ఇది సెట్టింగ్లలో ఎనేబుల్ చేసి ఉందని నిర్ధారించుకోండి."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"విడ్జెట్ల\" షార్ట్కట్ను జోడించడానికి, సెట్టింగ్లలో \"లాక్ స్క్రీన్లో విడ్జెట్లను చూపండి\" అనే ఆప్షన్ను ఎనేబుల్ చేసినట్లు నిర్ధారించుకోండి."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"సెట్టింగ్లు"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"వినియోగదారుని మార్చు"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"పుల్డౌన్ మెనూ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ఈ సెషన్లోని అన్ని యాప్లు మరియు డేటా తొలగించబడతాయి."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"నోటిఫికేషన్లు"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"సంభాషణలు"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"అన్ని నిశ్శబ్ద నోటిఫికేషన్లను క్లియర్ చేస్తుంది"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"అంతరాయం కలిగించవద్దు ద్వారా నోటిఫికేషన్లు పాజ్ చేయబడ్డాయి"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{నోటిఫికేషన్లు ఏవీ లేవు}=1{{mode} ద్వారా నోటిఫికేషన్లు పాజ్ చేయబడ్డాయి}=2{నోటిఫికేషన్లు, {mode}, మరో ఒక మోడ్ ద్వారా పాజ్ చేయబడ్డాయి}other{నోటిఫికేషన్లు, {mode}, మరో # మోడ్ల ద్వారా పాజ్ చేయబడ్డాయి}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"ఇప్పుడే ప్రారంభించండి"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"హెడ్ ట్రాకింగ్"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"రింగర్ మోడ్ను మార్చడానికి ట్యాప్ చేయండి"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"రింగర్ మోడ్"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"మ్యూట్ చేయి"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"అన్మ్యూట్ చేయి"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"వైబ్రేట్"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"స్ప్లిట్ స్క్రీన్ ఉపయోగిస్తున్నప్పుడు ఎడమ లేదా పైన యాప్నకు మారండి"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"స్ప్లిట్ స్క్రీన్ సమయంలో: ఒక దాన్నుండి మరో దానికి యాప్ రీప్లేస్ చేయండి"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"యాక్టివ్ విండోను డిస్ప్లేల మధ్య తరలించండి"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"విండోను ఎడమ వైపునకు తరలించండి"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"విండోను కుడి వైపునకు తరలించండి"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"విండోను విస్తరించండి"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"విండోను కుదించండి"</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>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"సిస్టమ్ కంట్రోల్స్"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"సిస్టమ్ యాప్లు"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"మల్టీ-టాస్కింగ్"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"ఇటీవలి యాప్లు"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"స్ప్లిట్ స్క్రీన్"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ఇన్పుట్"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"యాప్ షార్ట్కట్లు"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"కీబోర్డ్ షార్ట్కట్లు"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"కీబోర్డ్ షార్ట్కట్లను అనుకూలంగా మార్చండి"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"షార్ట్కట్ను తీసివేయాలా?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"తిరిగి ఆటోమేటిక్ సెట్టింగ్కు రీసెట్ చేయాలా?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"షార్ట్కట్ను కేటాయించడానికి కీని నొక్కండి"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ఇది మీ అనుకూల షార్ట్కట్ను శాశ్వతంగా తొలగిస్తుంది."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ఇది మీ అనుకూల షార్ట్కట్లన్నింటిని శాశ్వతంగా తొలగిస్తుంది."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"షార్ట్కట్లను వెతకండి"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"సెర్చ్ ఫలితాలు ఏవీ లేవు"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"కుదించండి చిహ్నం"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"యాక్షన్ లేదా మెటా కీ చిహ్నం"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ప్లస్ చిహ్నం"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"అనుకూలంగా మార్చండి"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"పూర్తయింది"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"విస్తరించండి చిహ్నం"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"లేదా"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"ప్లస్ గుర్తు"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"ఫార్వర్డ్ స్లాష్"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"లాగే హ్యాండిల్"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"కీబోర్డ్ సెట్టింగ్లు"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"షార్ట్కట్ను సెట్ చేయండి"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"తీసివేయండి"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"అవును, రీసెట్ చేయాలి"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"రద్దు చేయండి"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"కీని నొక్కండి"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"కీ కాంబినేషన్ ఇప్పటికే వినియోగంలో ఉంది. వేరొక కీని ట్రై చేయండి."</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"మీ కీబోర్డ్లో యాక్షన్ కీని నొక్కండి"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"చక్కగా చేశారు!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"అన్ని యాప్లను చూడడానికి ఉపయోగించే సంజ్ఞకు సంబంధించిన ట్యుటోరియల్ను మీరు పూర్తి చేశారు"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 6ca6968..fe13888 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"หากต้องการเปิดแอปโดยใช้วิดเจ็ต คุณจะต้องยืนยันตัวตนของคุณ นอกจากนี้ โปรดทราบว่าผู้อื่นจะดูวิดเจ็ตเหล่านี้ได้แม้ว่าแท็บเล็ตจะล็อกอยู่ก็ตาม วิดเจ็ตบางอย่างอาจไม่ได้มีไว้สำหรับหน้าจอล็อกของคุณ และอาจไม่ปลอดภัยที่จะเพิ่มที่นี่"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"รับทราบ"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"วิดเจ็ต"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"หากต้องการเพิ่มวิดเจ็ตในหน้าจอล็อกเป็นทางลัด โปรดตรวจสอบว่าได้เปิดใช้วิดเจ็ตแล้วในการตั้งค่า"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"หากต้องการเพิ่มทางลัด \"วิดเจ็ต\" โปรดตรวจสอบว่าได้เปิดใช้ \"แสดงวิดเจ็ตในหน้าจอล็อก\" แล้วในการตั้งค่า"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"การตั้งค่า"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"สลับผู้ใช้"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"เมนูแบบเลื่อนลง"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ระบบจะลบแอปและข้อมูลทั้งหมดในเซสชันนี้"</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"การแจ้งเตือน"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"การสนทนา"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ล้างการแจ้งเตือนแบบไม่มีเสียงทั้งหมด"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"หยุดการแจ้งเตือนชั่วคราวโดย \"ห้ามรบกวน\""</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{ไม่มีการแจ้งเตือน}=1{หยุดการแจ้งเตือนชั่วคราวโดย {mode}}=2{หยุดการแจ้งเตือนชั่วคราวโดย {mode} และโหมดอื่นอีก 1 โหมด}other{หยุดการแจ้งเตือนชั่วคราวโดย {mode} และโหมดอื่นอีก # โหมด}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"เริ่มเลย"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"การติดตามการเคลื่อนไหวของศีรษะ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"แตะเพื่อเปลี่ยนโหมดเสียงเรียกเข้า"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"โหมดเสียงเรียกเข้า"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ปิดเสียง"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"เปิดเสียง"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"สั่น"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"เปลี่ยนไปใช้แอปทางด้านซ้ายหรือด้านบนขณะใช้โหมดแยกหน้าจอ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"ระหว่างใช้โหมดแยกหน้าจอ: เปลี่ยนแอปหนึ่งเป็นอีกแอปหนึ่ง"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"ย้ายหน้าต่างที่ใช้งานไปยังหน้าจอต่างๆ"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"ย้ายหน้าต่างไปทางซ้าย"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"ย้ายหน้าต่างไปทางขวา"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"ขยายหน้าต่างเต็มหน้าจอ"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"ย่อหน้าต่าง"</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>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"การควบคุมระบบ"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"แอประบบ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"การทํางานหลายอย่างพร้อมกัน"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"แอปล่าสุด"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"แยกหน้าจอ"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"อินพุต"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"แป้นพิมพ์ลัดของแอป"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"แป้นพิมพ์ลัด"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ปรับแต่งแป้นพิมพ์ลัด"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"นำแป้นพิมพ์ลัดออกใช่ไหม"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"รีเซ็ตกลับเป็นค่าเริ่มต้นไหม"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"กดแป้นเพื่อกำหนดแป้นพิมพ์ลัด"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"การดำเนินการนี้จะลบแป้นพิมพ์ลัดที่กำหนดเองอย่างถาวร"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"การดำเนินการนี้จะลบทางลัดที่กำหนดเองทั้งหมดอย่างถาวร"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ค้นหาแป้นพิมพ์ลัด"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ไม่พบผลการค้นหา"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ไอคอนยุบ"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"ไอคอนการดำเนินการหรือแป้น Meta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"ไอคอนเครื่องหมายบวก"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ปรับแต่ง"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"เสร็จสิ้น"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ไอคอนขยาย"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"หรือ"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"บวก"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"เครื่องหมายทับ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"แฮนเดิลการลาก"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"การตั้งค่าแป้นพิมพ์"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ตั้งค่าแป้นพิมพ์ลัด"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"นำออก"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ใช่ รีเซ็ต"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ยกเลิก"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"กดแป้น"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"มีการใช้แป้นที่กดร่วมกันนี้แล้ว โปรดลองใช้แป้นอื่น"</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"กดปุ่มดำเนินการบนแป้นพิมพ์"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ยอดเยี่ยม"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"คุณทำท่าทางสัมผัสเพื่อดูแอปทั้งหมดสำเร็จแล้ว"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 2e1d286..9d79424 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para magbukas ng app gamit ang isang widget, kakailanganin mong i-verify na ikaw iyan. Bukod pa rito, tandaang puwedeng tingnan ng kahit na sino ang mga ito, kahit na naka-lock ang iyong tablet. Posibleng hindi para sa iyong lock screen ang ilang widget at posibleng hindi ligtas ang mga ito na idagdag dito."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Mga Widget"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Para magdagdag ng Mga Widget sa lock screen bilang shortcut, tiyaking naka-enable ito sa mga setting."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"Para idagdag ang shortcut na \"Mga Widget,\" tiyaking naka-enable ang \"Ipakita ang mga widget sa lock screen\" sa mga setting."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Mga Setting"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Magpalit ng user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ide-delete ang lahat ng app at data sa session na ito."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Mga Notification"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Mga Pag-uusap"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"I-clear ang lahat ng silent na notification"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Mga notification na na-pause ng Huwag Istorbohin"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Walang notification}=1{Na-pause ng {mode} ang mga notification}=2{Na-pause ng {mode} at isa pang mode ang mga notification}one{Na-pause ng {mode} at # pang mode ang mga notification}other{Na-pause ng {mode} at # pang mode ang mga notification}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Magsimula ngayon"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Pag-track ng Ulo"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"I-tap para baguhin ang ringer mode"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"i-mute"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"i-unmute"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"i-vibrate"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Lumipat sa app sa kaliwa o itaas habang ginagamit ang split screen"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Habang nasa split screen: magpalit-palit ng app"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Ilipat ang aktibong window sa pagitan ng mga display"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Ilipat ang window sa kaliwa"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Ilipat ang window sa kanan"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"I-maximize ang window"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"I-minimize ang window"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Lumipat sa susunod na wika"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Lumipat sa dating wika"</string>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Mga kontrol ng system"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Mga system app"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Pag-multitask"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Mga kamakailang app"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Mga shortcut ng app"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Mga keyboard shortcut"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"I-customize ang mga keyboard shortcut"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Alisin ang shortcut?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"I-reset pabalik sa default?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Pindutin ang key para magtalaga ng shortcut"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Permanente nitong ide-delete ang iyong custom na shortcut."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Permanente nitong ide-delete ang lahat ng iyong custom na shortcut."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Mga shortcut ng paghahanap"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Walang resulta ng paghahanap"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"I-collapse ang icon"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Icon ng Action o Meta key"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Icon na plus"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"I-customize"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Tapos na"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"I-expand ang icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"forward slash"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handle sa pag-drag"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Mga Setting ng Keyboard"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Magtakda ng shortcut"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Alisin"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Oo, i-reset"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Kanselahin"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pindutin ang key"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Ginagamit na ang kumbinasyon ng key. Sumubok ng ibang key."</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pindutin ang action key sa iyong keyboard"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Magaling!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Nakumpleto mo ang galaw sa pag-view ng lahat ng app"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Backlight ng keyboard"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d sa %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Mga Home Control"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 6628d23..046d973 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Widget kullanarak bir uygulamayı açmak için kimliğinizi doğrulamanız gerekir. Ayrıca, tabletiniz kilitliyken bile widget\'ların herkes tarafından görüntülenebileceğini unutmayın. Bazı widget\'lar kilit ekranınız için tasarlanmamış olabileceğinden buraya eklenmeleri güvenli olmayabilir."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Anladım"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Widget\'lar"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Kilit ekranında widget\'lar özelliğini kısayol olarak eklemek için ayarlarda bu özelliğin etkinleştirildiğinden emin olun."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kullanıcı değiştirme"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"açılır menü"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu oturumdaki tüm uygulamalar ve veriler silinecek."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Bildirimler"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Görüşmeler"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Sessiz bildirimlerin tümünü temizle"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Bildirimler, Rahatsız Etmeyin özelliği tarafından duraklatıldı"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Bildirim yok}=1{Bildirimler {mode} tarafından duraklatıldı}=2{Bildirimler, {mode} ve bir diğer mod tarafından duraklatıldı}other{Bildirimler, {mode} ve # diğer mod tarafından duraklatıldı}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Şimdi başlat"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Baş Takibi"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Telefon zili modunu değiştirmek için dokunun"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"telefon zili modu"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"sesi kapat"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"sesi aç"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"titreşim"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Bölünmüş ekran kullanırken soldaki veya üstteki uygulamaya geçiş yapın"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Bölünmüş ekran etkinken: Bir uygulamayı başkasıyla değiştir"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Etkin pencereyi ekranlar arasında taşıma"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Pencereyi sola taşı"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Pencereyi sağa taşı"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Pencereyi ekranı kaplayacak şekilde büyüt"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Pencereyi simge durumuna küçült"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Giriş"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Sonraki dile geç"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Önceki dile geç"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Sistem kontrolleri"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistem uygulamaları"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Çoklu görev"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Son uygulamalar"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Bölünmüş ekran"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Giriş"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Uygulama kısayolları"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Klavye kısayolları"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Klavye kısayollarını özelleştirin"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Kısayol kaldırılsın mı?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Kısayol atamak için tuşa basın"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Bu işlem, özel kısayolunuzu kalıcı olarak siler."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Arama kısayolları"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Arama sonucu yok"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Daralt simgesi"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"İşlem veya Meta tuşu simgesi"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Artı simgesi"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Özelleştir"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Bitti"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Genişlet simgesi"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"veya"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"artı"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"eğik çizgi"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Sürükleme tutamacı"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klavye Ayarları"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Kısayol ayarla"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Kaldır"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"İptal"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tuşa basın"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tuş kombinasyonu zaten kullanılıyor. Başka bir tuş deneyin."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Klavyenizde eylem tuşuna basın"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Tebrikler!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Tüm uygulamaları görüntüleme hareketini tamamladınız"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 71845a350..a414824 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Щоб відкрити додаток за допомогою віджета, вам потрібно буде підтвердити особу. Пам’ятайте також, що бачити віджети можуть усі, навіть коли планшет заблоковано. Можливо, деякі віджети не призначені для заблокованого екрана, і додавати їх на нього може бути небезпечно."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Віджети"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Щоб додати ярлик для опції \"Показувати віджети на заблокованому екрані\", переконайтеся, що її ввімкнено в налаштуваннях."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Змінити користувача"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"спадне меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усі додатки й дані з цього сеансу буде видалено."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Сповіщення"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Розмови"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Очистити всі беззвучні сповіщення"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Режим \"Не турбувати\" призупинив сповіщення"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Немає сповіщень}=1{Режим \"{mode}\" призупинив надсилання сповіщень}=2{\"{mode}\" і ще один режим призупинили надсилання сповіщень}one{\"{mode}\" і ще # режим призупинили надсилання сповіщень}few{\"{mode}\" і ще # режими призупинили надсилання сповіщень}many{\"{mode}\" і ще # режимів призупинили надсилання сповіщень}other{\"{mode}\" і ще # режиму призупинили надсилання сповіщень}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Почати зараз"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Відстеження рухів голови"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Торкніться, щоб змінити режим дзвінка"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"режим дзвінка"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"вимкнути звук"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"увімкнути звук"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"увімкнути вібросигнал"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Під час розділення екрана перемикатися на додаток ліворуч або вгорі"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Під час розділення екрана: замінити додаток іншим"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Перемістити активне вікно між дисплеями"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Перемістити вікно ліворуч"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Перемістити вікно праворуч"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Розгорнути вікно"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Згорнути вікно"</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>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Елементи керування системою"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системні додатки"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Багатозадачність"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Нещодавні додатки"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Розділити екран"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Введення"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Комбінації клавіш для додатків"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Комбінації клавіш"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Налаштуйте комбінації клавіш"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Видалити комбінацію клавіш?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Натисніть клавішу, щоб призначити комбінацію клавіш"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Вашу власну комбінацію клавіш буде видалено назавжди."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Комбінації клавіш для пошуку"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Нічого не знайдено"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок згортання"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Значок клавіші дії або метаклавіші"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Значок \"плюс\""</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Налаштувати"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок розгортання"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"плюс"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"скісна риска"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер переміщення"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Налаштування клавіатури"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Налаштувати комбінацію клавіш"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Видалити"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Скасувати"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Натисніть клавішу"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Комбінація клавіш уже використовується. Спробуйте іншу клавішу."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Натисніть клавішу дії на клавіатурі"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Чудово!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Ви виконали жест для перегляду всіх додатків"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 514244f..2980b2f 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ویجیٹ کے ذریعے ایپ کھولنے کے لیے آپ کو تصدیق کرنی ہوگی کہ یہ آپ ہی ہیں۔ نیز، ذہن میں رکھیں کہ کوئی بھی انہیں دیکھ سکتا ہے، یہاں تک کہ جب آپ کا ٹیبلیٹ مقفل ہو۔ ہو سکتا ہے کچھ ویجٹس آپ کی لاک اسکرین کے لیے نہ بنائے گئے ہوں اور یہاں شامل کرنا غیر محفوظ ہو سکتا ہے۔"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"سمجھ آ گئی"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ویجیٹس"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"مقفل اسکرین پر ویجیٹس کو شارٹ کٹ کے بطور شامل کرنے کے لیے یقینی بنائیں کہ یہ ترتیبات میں فعال ہے۔"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"\"ویجیٹس\" شارٹ کٹ شامل کرنے کے لیے، یقینی بنائیں کہ \"مقفل اسکرین پر ویجیٹس دکھائیں\" ترتیبات میں فعال ہے۔"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"ترتیبات"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"صارف سوئچ کریں"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"پل ڈاؤن مینیو"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"اس سیشن میں موجود سبھی ایپس اور ڈیٹا کو حذف کر دیا جائے گا۔"</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"اطلاعات"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"گفتگوئیں"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"سبھی خاموش اطلاعات کو صاف کریں"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'ڈسٹرب نہ کریں\' کے ذریعے اطلاعات کو موقوف کیا گیا"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{کوئی اطلاع نہیں ہے}=1{{mode} کی طرف سے اطلاعات کو روک دیا گیا ہے}=2{{mode} اور ایک دوسرے موڈ کے ذریعہ اطلاعات کو روک دیا گیا ہے}other{{mode} اور # دیگر طریقوں کے ذریعے اطلاعات کو روک دیا گیا ہے}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"ابھی شروع کریں"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"سر کی ٹریکنگ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"رنگر وضع تبدیل کرنے کیلئے تھپتھپائیں"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"رنگر موڈ"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"خاموش کریں"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"غیر خاموش کریں"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"وائبریٹ"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"اسپلٹ اسکرین کا استعمال کرتے ہوئے بائیں یا اوپر ایپ پر سوئچ کریں"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"اسپلٹ اسکرین کے دوران: ایک ایپ کو دوسرے سے تبدیل کریں"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"فعال ونڈو کو ڈسپلیز کے مابین منتقل کریں"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"ونڈو کو دائیں طرف منتقل کریں"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"ونڈو کو بائیں طرف منتقل کریں"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"ونڈو کو بڑا کریں"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"ونڈو کو چھوٹا کریں"</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>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"سسٹم کنٹرولز"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"سسٹم ایپس"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ملٹی ٹاسکنگ"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"حالیہ ایپس"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"اسپلٹ اسکرین"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ان پٹ"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ایپ شارٹ کٹس"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"کی بورڈ شارٹ کٹس"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"کی بورڈ شارٹ کٹس کو حسب ضرورت بنائیں"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"شارٹ کٹ ہٹائیں؟"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ڈیفالٹ پر واپس ری سیٹ کریں؟"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"شارٹ کٹ تفویض کرنے کے لیے کلید کو دبائیں"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"یہ آپ کا حسب ضرورت شارٹ کٹ مستقل طور پر حذف کر دے گا۔"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"یہ آپ کے تمام حسب ضرورت شارٹ کٹس کو مستقل طور پر حذف کر دے گا۔"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"تلاش کے شارٹ کٹس"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"تلاش کا کوئی نتیجہ نہیں ہے"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"آئیکن سکیڑیں"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"کارروائی یا Meta کلید کا آئیکن"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"پلس کا آئیکن"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"حسب ضرورت بنائیں"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ہو گیا"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"آئیکن پھیلائیں"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"پلس"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"فارورڈ سلیش"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"گھسیٹنے کا ہینڈل"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"کی بورڈ کی ترتیبات"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"شارٹ کٹ سیٹ کریں"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ہٹائیں"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ہاں، ری سیٹ کریں"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"منسوخ کریں"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"کلید کو دبائیں"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"کلیدی مجموعہ پہلے سے استعمال میں ہے۔ دوسری کلید آزمائیں۔"</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"اپنے کی بورڈ پر ایکشن کلید دبائیں"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"بہت خوب!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"آپ نے سبھی ایپس دیکھیں کا اشارہ مکمل کر لیا ہے"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 777be07..9057926 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ilovani vidjet orqali ochish uchun shaxsingizni tasdiqlashingiz kerak. Shuningdek, planshet qulflanganda ham bu axborotlar hammaga koʻrinishini unutmang. Ayrim vidjetlar ekran qulfiga moslanmagan va ularni bu yerda chiqarish xavfli boʻlishi mumkin."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Vidjetlar"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Ekran qulfiga yorliq sifatida vidjetlar kiritish uchun uning sozlamalarda yoqilganini tekshiring."</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"“Vidjetlar” yorligʻini qoʻshish uchun sozlamalarda “Vidjetlarni ekran qulfida chiqarish” yoqilganini tekshiring."</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"Sozlamalar"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Foydalanuvchini almashtirish"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"tortib tushiriladigan menyu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ushbu seansdagi barcha ilovalar va ma’lumotlar o‘chirib tashlanadi."</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Bildirishnomalar"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Suhbatlar"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Barcha sokin bildirishnomalarni tozalash"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Bezovta qilinmasin rejimida bildirishnomalar pauza qilinadi"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Bildirishnomalar yoʻq}=1{{mode} rejimi bildirishnomalarni pauza qilgan}=2{{mode} va yana bitta boshqa rejim bildirishnomalarni pauza qilgan}other{{mode} va # ta boshqa rejim bildirishnomalarni pauza qilgan}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Boshlash"</string>
@@ -704,6 +707,7 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Boshni kuzatish"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Jiringlagich rejimini oʻzgartirish uchun bosing"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"jiringlagich rejimi"</string>
+ <string name="volume_ringer_drawer_closed_content_description" msgid="4737792429808781745">"<xliff:g id="VOLUME_RINGER_STATUS">%1$s</xliff:g>, jiringlagich rejimini oʻzgartirish uchun bosing"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ovozsiz qilish"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ovozni yoqish"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"tebranish"</string>
@@ -875,14 +879,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Ajratilgan ekranda chapdagi yoki yuqoridagi ilovaga almashish"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Ajratilgan rejimda ilovalarni oʻzaro almashtirish"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Faol oynani ekranlararo koʻchirish"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Oynani chapga surish"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Oynani oʻngga surish"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Oynani yoyish"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Oynani kichraytirish"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Kiritish"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Keyingi tilga almashtirish"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Avvalgi tilga almashtirish"</string>
@@ -1419,7 +1419,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Tizim boshqaruvi tugmalari"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Tizim ilovalari"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multi-vazifalilik"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Oxirgi ilovalar"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ekranni ikkiga ajratish"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Kiritish"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Ilova yorliqlari"</string>
@@ -1428,25 +1427,27 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tezkor tugmalar"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tezkor tugmalarni moslash"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Tezkor tugma olib tashlansinmi?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Asliga qaytarilsinmi?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Tezkor tugma sozlash uchun tugmani bosing"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Bunda maxsus tezkor tugma butunlay oʻchirib tashlanadi."</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Bunda barcha maxsus yorliqlaringiz butunlay oʻchirib tashlanadi."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tezkor tugmalar qidiruvi"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Hech narsa topilmadi"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Yigʻish belgisi"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Amal bajarish uchun Meta tugmasi belgisi"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plus belgisi"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Moslash"</string>
+ <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Tiklash"</string>
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Tayyor"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Yoyish belgisi"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"yoki"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"plus"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"oldinga qiya chiziq"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Surish dastagi"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatura sozlamalari"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Tezkor tugma sozlash"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Olib tashlash"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ha, asliga qaytarilsin"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Bekor qilish"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tugmani bosing"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Bu tugmalar birikmasi band. Boshqasini ishlating."</string>
@@ -1478,6 +1479,7 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Klaviaturadagi amal tugmasini bosing"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Barakalla!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Hamma ilovalarni koʻrish ishorasini tugalladingiz"</string>
+ <string name="tutorial_animation_content_description" msgid="2698816574982370184">"Qoʻllanma animatsiyasi, pauza qilish va ijroni davom ettirish uchun bosing."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura orqa yoritkichi"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Daraja: %1$d / %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Uy boshqaruvi"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 6ea683a..6c7c5f6 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Để dùng tiện ích mở một ứng dụng, bạn cần xác minh danh tính của mình. Ngoài ra, hãy lưu ý rằng bất kỳ ai cũng có thể xem các tiện ích này, ngay cả khi máy tính bảng của bạn được khoá. Một số tiện ích có thể không dành cho màn hình khoá và không an toàn khi thêm vào đây."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Tôi hiểu"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Tiện ích"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Để thêm Tiện ích dưới dạng lối tắt trên màn hình khoá, hãy đảm bảo bạn đã bật tính năng này trong phần cài đặt."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Chuyển đổi người dùng"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"trình đơn kéo xuống"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tất cả ứng dụng và dữ liệu trong phiên này sẽ bị xóa."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Thông báo"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Cuộc trò chuyện"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Xóa tất cả thông báo im lặng"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Chế độ Không làm phiền đã tạm dừng thông báo"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Không có thông báo}=1{{mode} đã tạm dừng thông báo}=2{{mode} và một chế độ khác đã tạm dừng thông báo}other{{mode} và # chế độ khác đã tạm dừng thông báo}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Bắt đầu ngay"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Theo dõi chuyển động của đầu"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Nhấn để thay đổi chế độ chuông"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"chế độ chuông"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"tắt tiếng"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"bật tiếng"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"rung"</string>
@@ -875,14 +882,10 @@
<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>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Trong chế độ chia đôi màn hình: thay một ứng dụng bằng ứng dụng khác"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Di chuyển cửa sổ đang hoạt động giữa các màn hình"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Di chuyển cửa sổ sang trái"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Di chuyển cửa sổ sang phải"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Phóng to cửa sổ"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Thu nhỏ cửa sổ"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Đầu vào"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Chuyển sang ngôn ngữ tiếp theo"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Chuyển về ngôn ngữ trước"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Điều khiển hệ thống"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Ứng dụng hệ thống"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Đa nhiệm"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Ứng dụng gần đây"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Chia đôi màn hình"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Phương thức nhập"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Lối tắt ứng dụng"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Phím tắt"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tuỳ chỉnh phím tắt"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Xoá lối tắt?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Nhấn phím để chỉ định lối tắt"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Thao tác này sẽ xoá vĩnh viễn lối tắt tuỳ chỉnh của bạn."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tìm lối tắt"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Không có kết quả tìm kiếm nào"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Biểu tượng Thu gọn"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Biểu tượng phím Meta (phím hành động)"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Biểu tượng dấu cộng"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Tuỳ chỉnh"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Xong"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Biểu tượng Mở rộng"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"hoặc"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"dấu cộng"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"dấu gạch chéo lên"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Nút kéo"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Cài đặt bàn phím"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Đặt phím tắt"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Xoá"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Huỷ"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Nhấn phím"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tổ hợp phím đã được sử dụng. Hãy thử một phím khác."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Nhấn phím hành động trên bàn phím"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Rất tốt!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Bạn đã hoàn tất cử chỉ xem tất cả các ứng dụng"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Đèn nền bàn phím"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Độ sáng %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Điều khiển nhà"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index bc9c318..d37fe90 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -529,7 +529,8 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"若要使用微件打开应用,您需要验证是您本人在操作。另外请注意,任何人都可以查看此类微件,即使您的平板电脑已锁定。有些微件可能不适合显示在锁定的屏幕中,因此添加到这里可能不安全。"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"知道了"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"微件"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"如要将微件作为快捷方式添加到锁屏界面,请确保已在设置中启用该功能。"</string>
+ <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"如要添加“微件”快捷方式,请确保已在设置中启用“在锁屏状态下显示微件”。"</string>
+ <string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"设置"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切换用户"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉菜单"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"此会话中的所有应用和数据都将被删除。"</string>
@@ -590,6 +591,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"通知"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"对话"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"清除所有静音通知"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"勿扰模式暂停的通知"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{无通知}=1{{mode}暂停了通知}=2{{mode}和另外 1 种模式暂停了通知}other{{mode}和另外 # 种模式暂停了通知}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"立即开始"</string>
@@ -704,6 +707,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"头部跟踪"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"点按即可更改振铃器模式"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"响铃模式"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"静音"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消静音"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"振动"</string>
@@ -875,14 +880,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"使用分屏模式时,切换到左侧或上方的应用"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"在分屏期间:将一个应用替换为另一个应用"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"在各个显示屏之间移动活动窗口"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"将窗口移至左侧"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"将窗口移至右侧"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"最大化窗口"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"最小化窗口"</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>
@@ -1419,7 +1420,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"系统控件"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"系统应用"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"多任务处理"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"最近用过的应用"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"分屏"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"输入"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"应用快捷键"</string>
@@ -1428,25 +1428,28 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"键盘快捷键"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"自定义键盘快捷键"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"要移除快捷键吗?"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"要重置为默认快捷方式吗?"</string>
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"按下按键即可指定快捷键"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"此操作会永久删除您的自定义快捷键。"</string>
+ <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"此操作会永久删除您的所有自定义快捷方式。"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜索快捷键"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"无搜索结果"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收起图标"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"操作键或元键图标"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"加号图标"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"自定义"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"完成"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展开图标"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"加号"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"正斜线"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖动手柄"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"键盘设置"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"设置快捷键"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"移除"</string>
+ <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"是,重置"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"取消"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"按下按键"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"按键组合已被使用,请尝试使用其他按键。"</string>
@@ -1478,6 +1481,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按键盘上的快捷操作按键"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"非常棒!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"您已完成“查看所有应用”手势教程"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 2147743..9b16360 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -449,8 +449,8 @@
<string name="zen_modes_dialog_title" msgid="8854640808100096934">"模式"</string>
<string name="zen_modes_dialog_done" msgid="6654130880256438950">"完成"</string>
<string name="zen_modes_dialog_settings" msgid="2310248023728936697">"設定"</string>
- <string name="zen_mode_on" msgid="9085304934016242591">"開啟"</string>
- <string name="zen_mode_on_with_details" msgid="7416143430557895497">"開 • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="zen_mode_on" msgid="9085304934016242591">"已開啟"</string>
+ <string name="zen_mode_on_with_details" msgid="7416143430557895497">"已開啟 • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="zen_mode_off" msgid="1736604456618147306">"關閉"</string>
<string name="zen_mode_set_up" msgid="8231201163894922821">"未設定"</string>
<string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"在「設定」中管理"</string>
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"如要使用小工具開啟應用程式,系統會要求你驗證身分。請注意,所有人都能查看小工具,即使平板電腦已鎖定亦然。部分小工具可能不適用於上鎖畫面,新增至這裡可能會有安全疑慮。"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"知道了"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"小工具"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"如要將小工具新增為上鎖畫面上的捷徑,請確認已在設定中啟用此功能。"</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉式選單"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會被刪除。"</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"通知"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"對話"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"清除所有靜音通知"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"「請勿騷擾」模式已將通知暫停"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{沒有通知}=1{{mode}已暫停通知}=2{{mode}和另外一個模式已暫停通知}other{{mode}和另外 # 個模式已暫停通知}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"立即開始"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"頭部追蹤"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"輕按即可變更響鈴模式"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"響鈴模式"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"靜音"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消靜音"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"震動"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"使用分割螢幕時,切換至左邊或上方的應用程式"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"使用分割螢幕期間:更換應用程式"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"在不同畫面間移動使用中的視窗"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"將視窗移到左邊"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"將視窗移到右邊"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"將視窗放到最大"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"將視窗縮到最小"</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>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"系統控制項"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"系統應用程式"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"多工處理"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"最近使用的應用程式"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"分割螢幕"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"輸入"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"應用程式捷徑"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"鍵盤快速鍵"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"自訂鍵盤快速鍵"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"要移除快速鍵嗎?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"按鍵即可指派快速鍵"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"這將永久刪除你的自訂快速鍵。"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"沒有相符的搜尋結果"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"快捷操作鍵或修飾鍵圖示"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"加號圖示"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"自訂"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"完成"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"加"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"正斜線"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖曳控點"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"鍵盤設定"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"設定快速鍵"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"移除"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"取消"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"按鍵"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"此按鍵組合已在使用,請改用其他按鍵。"</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按下鍵盤上的快捷操作鍵"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"做得好!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"你已完成「查看所有應用程式」手勢的教學課程"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 13fb81c..49111ea 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"如要使用小工具開啟應用程式,需先驗證身分。請留意,即使平板電腦已鎖定,所有人都還是能查看小工具。某些小工具可能不適用於螢幕鎖定畫面,新增到此可能會有安全疑慮。"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"我知道了"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"小工具"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"如要將小工具新增為螢幕鎖定畫面上的捷徑,請確認已在設定中啟用這項功能。"</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉式選單"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會刪除。"</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"通知"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"對話"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"清除所有靜音通知"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"「零打擾」模式已將通知設為暫停"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{沒有通知}=1{「{mode}」模式已將通知設為暫停}=2{「{mode}」和另一個模式已將通知設為暫停}other{「{mode}」和另外 # 個模式已將通知設為暫停}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"立即開始"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"頭部追蹤"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"輕觸即可變更鈴聲模式"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"鈴聲模式"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"靜音"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消靜音"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"震動"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"使用分割畫面時,切換到左邊或上方的應用程式"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"使用分割畫面期間:更換應用程式"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"在不同畫面間移動使用中的視窗"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"將視窗移至左側"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"將視窗移至右側"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"將視窗放到最大"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"將視窗縮到最小"</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>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"系統控制選項"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"系統應用程式"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"多工處理"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"最近使用的應用程式"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"分割畫面"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"輸入"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"應用程式捷徑"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"鍵盤快速鍵"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"自訂鍵盤快速鍵"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"要移除快速鍵嗎?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"按下按鍵即可指派快速鍵"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"這項操作會永久刪除自訂快速鍵。"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"找不到相符的搜尋結果"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"快捷操作鍵或修飾鍵圖示"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"加號圖示"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"自訂"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"完成"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"加"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"斜線"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖曳控點"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"鍵盤設定"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"設定快速鍵"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"移除"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"取消"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"按下按鍵"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"這個按鍵組合已在使用中,請改用其他按鍵。"</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按下鍵盤上的快捷操作鍵"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"非常好!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"你已完成「查看所有應用程式」手勢教學課程"</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<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>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index f323021..9d4f70c 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -529,7 +529,10 @@
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ukuze uvule i-app usebenzisa iwijethi, uzodinga ukuqinisekisa ukuthi nguwe. Futhi, khumbula ukuthi noma ubani angakwazi ukuzibuka, nanoma ithebhulethi yakho ikhiyiwe. Amanye amawijethi kungenzeka abengahloselwe ukukhiya isikrini sakho futhi kungenzeka awaphephile ukuthi angafakwa lapha."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ngiyezwa"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"Amawijethi"</string>
- <string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="511359420883794513">"Ukufaka Amawijethi esikrinini sokukhiya njengesinqamuleli, qinisekisa ukuthi inikwe amandla kumasethingi."</string>
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_disabled_text (599170482297578735) -->
+ <skip />
+ <!-- no translation found for glanceable_hub_lockscreen_affordance_action_button_label (7636151133344609375) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Shintsha umsebenzisi"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"imenyu yokudonsela phansi"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wonke ama-app nedatha kulesi sikhathi azosuswa."</string>
@@ -590,6 +593,8 @@
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Izaziso"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Izingxoxo"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Sula zonke izaziso ezithulile"</string>
+ <!-- no translation found for accessibility_notification_section_header_open_settings (6235202417954844004) -->
+ <skip />
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Izaziso zimiswe okwesikhashana ukungaphazamisi"</string>
<string name="modes_suppressing_shade_text" msgid="6037581130837903239">"{count,plural,offset:1 =0{Azikho izaziso}=1{Izaziso zimiswe okwesikhashana yi-{mode}}=2{Izaziso zimiswe okwesikhashana yi-{mode} nelinye imodi elilodwa}one{Izaziso zimiswe okwesikhashana yi-{mode} kanye namanye amamodi angu-#}other{Izaziso zimiswe okwesikhashana yi-{mode} kanye namanye amamodi angu-#}}"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Qala manje"</string>
@@ -704,6 +709,8 @@
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Ukulandelela Ikhanda"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Thepha ukuze ushintshe imodi yokukhala"</string>
<string name="volume_ringer_mode" msgid="6867838048430807128">"imodi yokukhala"</string>
+ <!-- no translation found for volume_ringer_drawer_closed_content_description (4737792429808781745) -->
+ <skip />
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"thulisa"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"susa ukuthula"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"dlidliza"</string>
@@ -875,14 +882,10 @@
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Shintshela ku-app ngakwesokunxele noma ngaphezulu ngenkathi usebenzisa ukuhlukanisa isikrini"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Ngesikhathi sokuhlukaniswa kwesikrini: shintsha i-app ngenye"</string>
<string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Hambisa iwindi elisebenzayo phakathi kwezibonisi"</string>
- <!-- no translation found for system_desktop_mode_snap_left_window (8636204689945162298) -->
- <skip />
- <!-- no translation found for system_desktop_mode_snap_right_window (2162560187639411929) -->
- <skip />
- <!-- no translation found for system_desktop_mode_toggle_maximize_window (4084100093691768239) -->
- <skip />
- <!-- no translation found for system_desktop_mode_minimize_window (1248714536732927092) -->
- <skip />
+ <string name="system_desktop_mode_snap_left_window" msgid="8636204689945162298">"Hambisa iwindi liye kwesokudla"</string>
+ <string name="system_desktop_mode_snap_right_window" msgid="2162560187639411929">"Hambisa iwindi liye kwesokudla"</string>
+ <string name="system_desktop_mode_toggle_maximize_window" msgid="4084100093691768239">"Khulisa iwindi"</string>
+ <string name="system_desktop_mode_minimize_window" msgid="1248714536732927092">"Nciphisa iwindi"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Okokufaka"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Shintshela olimini olulandelayo"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Shintshela olimini lwangaphambili"</string>
@@ -1419,7 +1422,6 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Izilawuli zesistimu"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Ama-app esistimu"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Ukwenza imisebenzi eminingi"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Ama-app wakamuva"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Hlukanisa isikrini"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Okokufaka"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Izinqamuleli Zohlelo lokusebenza"</string>
@@ -1428,25 +1430,31 @@
<string name="shortcut_helper_title" msgid="8567500639300970049">"Izinqamuleli zekhibhodi"</string>
<string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Hlela izinqamuleli zekhibhodi ngendlela oyifisayo"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Susa isinqamuleli?"</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_dialog_title (8131184731313717780) -->
+ <skip />
<string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Cindezela ukhiye ukuze unikeze isinqamuleli"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Lokhu kuzosula isinqamuleli sakho somuntu ngamunye unomphela."</string>
+ <!-- no translation found for shortcut_customize_mode_reset_shortcut_description (2081849715634358684) -->
+ <skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sesha izinqamuleli"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ayikho imiphumela yosesho"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Goqa isithonjana"</string>
<string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Isithonjana sesenzo noma seMeta"</string>
<string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Isithonjana sesengezo"</string>
<string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Enza ngendlela oyifisayo"</string>
+ <!-- no translation found for shortcut_helper_reset_button_text (2548243844050633472) -->
+ <skip />
<string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Kwenziwe"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Nweba isithonjana"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"noma"</string>
- <!-- no translation found for shortcut_helper_key_combinations_and_conjunction (6138186504075880224) -->
- <skip />
- <!-- no translation found for shortcut_helper_key_combinations_forward_slash (1238652537199346970) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_and_conjunction" msgid="6138186504075880224">"hlanganisa"</string>
+ <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"umugqa otshekele phambili"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Hudula isibambi"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Amasethingi Ekhibhodi"</string>
<string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Setha isinqamuleli"</string>
<string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Susa"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_reset_button_label (7645535254306312685) -->
+ <skip />
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Khansela"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Cindezela ukhiye"</string>
<string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Inhlanganisela yokhiye isiyasetshenziswa kakade. Zama omunye ukhiye."</string>
@@ -1478,6 +1486,8 @@
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Cindezela inkinobho yokufinyelela kukhibhodi yakho"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Wenze kahle!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Uqedele ukunyakazisa kokubuka onke ama-app."</string>
+ <!-- no translation found for tutorial_animation_content_description (2698816574982370184) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Ilambu lekhibhodi"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Ileveli %1$d ka-%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Izilawuli Zasekhaya"</string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 478050b..5894f29 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -237,6 +237,9 @@
<dimen name="status_bar_connected_device_bt_indicator_size">17dp</dimen>
<!-- Height of a small notification in the status bar (2025 redesign version) -->
+ <dimen name="notification_2025_header_height">@*android:dimen/notification_2025_header_height</dimen>
+
+ <!-- Height of a small notification in the status bar (2025 redesign version) -->
<dimen name="notification_2025_min_height">@*android:dimen/notification_2025_min_height</dimen>
<!-- Height of a small notification in the status bar-->
@@ -1221,6 +1224,9 @@
<dimen name="min_window_blur_radius">1px</dimen>
<dimen name="max_window_blur_radius">23px</dimen>
+ <!-- Blur radius behind Notification Shade -->
+ <dimen name="max_shade_window_blur_radius">60dp</dimen>
+
<!-- How much into a DisplayCutout's bounds we can go, on each side -->
<dimen name="display_cutout_margin_consumption">0px</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e417da4..05c13f2 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1342,6 +1342,8 @@
<string name="glanceable_hub_lockscreen_affordance_disabled_text">To add the \"Widgets\" shortcut, make sure \"Show widgets on lock screen\" is enabled in settings.</string>
<!-- Label for a button used to open Settings in order to enable showing widgets on the lock screen. [CHAR LIMIT=NONE] -->
<string name="glanceable_hub_lockscreen_affordance_action_button_label">Settings</string>
+ <!-- Content description for a "show screensaver" button on glanceable hub. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_glanceable_hub_to_dream_button">Show screensaver button</string>
<!-- Related to user switcher --><skip/>
@@ -1509,6 +1511,9 @@
<!-- Content description for accessibility: Tapping this button will dismiss all gentle notifications [CHAR LIMIT=NONE] -->
<string name="accessibility_notification_section_header_gentle_clear_all">Clear all silent notifications</string>
+ <!-- Content description for accessibility: Tapping this button will open notifications settings [CHAR LIMIT=NONE] -->
+ <string name="accessibility_notification_section_header_open_settings">Open notifications settings</string>
+
<!-- The text to show in the notifications shade when dnd is suppressing notifications. [CHAR LIMIT=100] -->
<string name="dnd_suppressing_shade_text">Notifications paused by Do Not Disturb</string>
@@ -1812,6 +1817,7 @@
<string name="volume_ringer_change">Tap to change ringer mode</string>
<string name="volume_ringer_mode">ringer mode</string>
+ <string name="volume_ringer_drawer_closed_content_description"><xliff:g id="volume ringer status" example="Ring">%1$s</xliff:g>, tap to change ringer mode </string>
<!-- Hint for accessibility. For example: double tap to mute [CHAR_LIMIT=NONE] -->
<string name="volume_ringer_hint_mute">mute</string>
@@ -2276,11 +2282,11 @@
<!-- User visible title for the multitasking keyboard shortcuts list. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_system_multitasking">Multitasking</string>
<!-- User visible title for the keyboard shortcut that enters split screen with current app on the right [CHAR LIMIT=70] -->
- <string name="system_multitasking_rhs">Use split screen with current app on the right</string>
+ <string name="system_multitasking_rhs">Use split screen with app on the right</string>
<!-- User visible title for the keyboard shortcut that enters split screen with current app on the left [CHAR LIMIT=70] -->
- <string name="system_multitasking_lhs">Use split screen with current app on the left</string>
+ <string name="system_multitasking_lhs">Use split screen with app on the left</string>
<!-- User visible title for the keyboard shortcut that switches from split screen to full screen [CHAR LIMIT=70] -->
- <string name="system_multitasking_full_screen">Switch from split screen to full screen</string>
+ <string name="system_multitasking_full_screen">Switch to full screen</string>
<!-- User visible title for the keyboard shortcut that switches to app on right or below while using split screen [CHAR LIMIT=70] -->
<string name="system_multitasking_splitscreen_focus_rhs">Switch to app on right or below while using split screen</string>
<!-- User visible title for the keyboard shortcut that switches to app on left or above while using split screen [CHAR LIMIT=70] -->
@@ -3949,6 +3955,8 @@
<string name="tutorial_action_key_success_title">Well done!</string>
<!-- Text shown to the user after they complete action key tutorial [CHAR LIMIT=NONE] -->
<string name="tutorial_action_key_success_body">You completed the view all apps gesture</string>
+ <!-- Content description for the animation playing during the tutorial. The user can click the animation to pause and unpause playback. [CHAR LIMIT=NONE] -->
+ <string name="tutorial_animation_content_description">Tutorial animation, click to pause and resume play.</string>
<!-- Content description for keyboard backlight brightness dialog [CHAR LIMIT=NONE] -->
<string name="keyboard_backlight_dialog_title">Keyboard backlight</string>
diff --git a/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags b/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags
index 08236b7..ca5424b 100644
--- a/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags
+++ b/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package com.android.systemui;
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index a46b236..9817322 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -46,6 +46,8 @@
import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.ConfigurationForwarder;
import com.android.systemui.util.NotificationChannels;
+import com.android.wm.shell.dagger.HasWMComponent;
+import com.android.wm.shell.dagger.WMComponent;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayDeque;
@@ -62,7 +64,7 @@
* Application class for SystemUI.
*/
public class SystemUIApplication extends Application implements
- SystemUIAppComponentFactoryBase.ContextInitializer {
+ SystemUIAppComponentFactoryBase.ContextInitializer, HasWMComponent {
public static final String TAG = "SystemUIService";
private static final boolean DEBUG = false;
@@ -490,4 +492,10 @@
n.addExtras(extras);
}
+
+ @NonNull
+ @Override
+ public WMComponent getWMComponent() {
+ return mInitializer.getWMComponent();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
index 5c75a49..f530522 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
@@ -24,9 +24,9 @@
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.dagger.SysUIComponent;
-import com.android.systemui.dagger.WMComponent;
import com.android.systemui.res.R;
import com.android.systemui.util.InitializationChecker;
+import com.android.wm.shell.dagger.WMComponent;
import com.android.wm.shell.dagger.WMShellConcurrencyModule;
import com.android.wm.shell.keyguard.KeyguardTransitions;
import com.android.wm.shell.shared.ShellTransitions;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index 1f21af8..ad12229 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -55,7 +55,6 @@
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.bluetooth.HapClientProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.systemui.accessibility.hearingaid.HearingDevicesListAdapter.HearingDeviceItemCallback;
@@ -67,7 +66,6 @@
import com.android.systemui.bluetooth.qsdialog.DeviceItemFactory;
import com.android.systemui.bluetooth.qsdialog.DeviceItemType;
import com.android.systemui.bluetooth.qsdialog.SavedHearingDeviceItemFactory;
-import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.res.R;
@@ -87,6 +85,7 @@
*/
public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
HearingDeviceItemCallback, BluetoothCallback {
+
private static final String TAG = "HearingDevicesDialogDelegate";
@VisibleForTesting
static final String ACTION_BLUETOOTH_DEVICE_DETAILS =
@@ -96,25 +95,27 @@
@VisibleForTesting
static final Intent LIVE_CAPTION_INTENT = new Intent(
"com.android.settings.action.live_caption");
+
private final SystemUIDialog.Factory mSystemUIDialogFactory;
private final DialogTransitionAnimator mDialogTransitionAnimator;
private final ActivityStarter mActivityStarter;
- private final boolean mShowPairNewDevice;
private final LocalBluetoothManager mLocalBluetoothManager;
private final Handler mMainHandler;
private final AudioManager mAudioManager;
private final LocalBluetoothProfileManager mProfileManager;
- private final HapClientProfile mHapClientProfile;
private final HearingDevicesUiEventLogger mUiEventLogger;
+ private final boolean mShowPairNewDevice;
private final int mLaunchSourceId;
- private HearingDevicesListAdapter mDeviceListAdapter;
- private HearingDevicesPresetsController mPresetsController;
- private Context mApplicationContext;
+
private SystemUIDialog mDialog;
+
private RecyclerView mDeviceList;
private List<DeviceItem> mHearingDeviceItemList;
+ private HearingDevicesListAdapter mDeviceListAdapter;
+
private View mPresetLayout;
private Spinner mPresetSpinner;
+ private HearingDevicesPresetsController mPresetController;
private HearingDevicesSpinnerAdapter mPresetInfoAdapter;
private final HearingDevicesPresetsController.PresetCallback mPresetCallback =
new HearingDevicesPresetsController.PresetCallback() {
@@ -122,20 +123,18 @@
public void onPresetInfoUpdated(List<BluetoothHapPresetInfo> presetInfos,
int activePresetIndex) {
mMainHandler.post(
- () -> refreshPresetInfoAdapter(presetInfos, activePresetIndex));
+ () -> refreshPresetUi(presetInfos, activePresetIndex));
}
@Override
public void onPresetCommandFailed(int reason) {
- final List<BluetoothHapPresetInfo> presetInfos =
- mPresetsController.getAllPresetInfo();
- final int activePresetIndex = mPresetsController.getActivePresetIndex();
+ mPresetController.refreshPresetInfo();
mMainHandler.post(() -> {
- refreshPresetInfoAdapter(presetInfos, activePresetIndex);
- showPresetErrorToast(mApplicationContext);
+ showErrorToast(R.string.hearing_devices_presets_error);
});
}
};
+
private final List<DeviceItemFactory> mHearingDeviceItemFactoryList = List.of(
new ActiveHearingDeviceItemFactory(),
new AvailableHearingDeviceItemFactory(),
@@ -159,7 +158,6 @@
@AssistedInject
public HearingDevicesDialogDelegate(
- @Application Context applicationContext,
@Assisted boolean showPairNewDevice,
@Assisted @HearingDevicesUiEventLogger.LaunchSourceId int launchSourceId,
SystemUIDialog.Factory systemUIDialogFactory,
@@ -169,7 +167,6 @@
@Main Handler handler,
AudioManager audioManager,
HearingDevicesUiEventLogger uiEventLogger) {
- mApplicationContext = applicationContext;
mShowPairNewDevice = showPairNewDevice;
mSystemUIDialogFactory = systemUIDialogFactory;
mActivityStarter = activityStarter;
@@ -178,7 +175,6 @@
mMainHandler = handler;
mAudioManager = audioManager;
mProfileManager = localBluetoothManager.getProfileManager();
- mHapClientProfile = mProfileManager.getHapClientProfile();
mUiEventLogger = uiEventLogger;
mLaunchSourceId = launchSourceId;
}
@@ -229,38 +225,26 @@
@Override
public void onActiveDeviceChanged(@Nullable CachedBluetoothDevice activeDevice,
int bluetoothProfile) {
- CachedBluetoothDevice activeHearingDevice;
- mHearingDeviceItemList = getHearingDevicesList();
- if (mPresetsController != null) {
- activeHearingDevice = getActiveHearingDevice(mHearingDeviceItemList);
- mPresetsController.setHearingDeviceIfSupportHap(activeHearingDevice);
- } else {
- activeHearingDevice = null;
+ refreshDeviceUi();
+ if (mPresetController != null) {
+ mPresetController.setDevice(getActiveHearingDevice());
+ mMainHandler.post(() -> {
+ mPresetLayout.setVisibility(
+ mPresetController.isPresetControlAvailable() ? VISIBLE : GONE);
+ });
}
- mMainHandler.post(() -> {
- mDeviceListAdapter.refreshDeviceItemList(mHearingDeviceItemList);
- final List<BluetoothHapPresetInfo> presetInfos =
- mPresetsController.getAllPresetInfo();
- final int activePresetIndex = mPresetsController.getActivePresetIndex();
- refreshPresetInfoAdapter(presetInfos, activePresetIndex);
- mPresetLayout.setVisibility(
- (activeHearingDevice != null && activeHearingDevice.isConnectedHapClientDevice()
- && !mPresetInfoAdapter.isEmpty()) ? VISIBLE : GONE);
- });
}
@Override
public void onProfileConnectionStateChanged(@NonNull CachedBluetoothDevice cachedDevice,
int state, int bluetoothProfile) {
- mHearingDeviceItemList = getHearingDevicesList();
- mMainHandler.post(() -> mDeviceListAdapter.refreshDeviceItemList(mHearingDeviceItemList));
+ refreshDeviceUi();
}
@Override
public void onAclConnectionStateChanged(@NonNull CachedBluetoothDevice cachedDevice,
int state) {
- mHearingDeviceItemList = getHearingDevicesList();
- mMainHandler.post(() -> mDeviceListAdapter.refreshDeviceItemList(mHearingDeviceItemList));
+ refreshDeviceUi();
}
@Override
@@ -306,13 +290,9 @@
if (mLocalBluetoothManager == null) {
return;
}
-
mLocalBluetoothManager.getEventManager().registerCallback(this);
- if (mPresetsController != null) {
- mPresetsController.registerHapCallback();
- if (mHapClientProfile != null && !mHapClientProfile.isProfileReady()) {
- mProfileManager.addServiceListener(mPresetsController);
- }
+ if (mPresetController != null) {
+ mPresetController.registerHapCallback();
}
}
@@ -322,37 +302,25 @@
return;
}
- if (mPresetsController != null) {
- mPresetsController.unregisterHapCallback();
- mProfileManager.removeServiceListener(mPresetsController);
+ if (mPresetController != null) {
+ mPresetController.unregisterHapCallback();
}
mLocalBluetoothManager.getEventManager().unregisterCallback(this);
}
- @VisibleForTesting
- void setHearingDevicesPresetsController(HearingDevicesPresetsController controller) {
- mPresetsController = controller;
- }
-
private void setupDeviceListView(SystemUIDialog dialog) {
mDeviceList.setLayoutManager(new LinearLayoutManager(dialog.getContext()));
- mHearingDeviceItemList = getHearingDevicesList();
+ mHearingDeviceItemList = getHearingDeviceItemList();
mDeviceListAdapter = new HearingDevicesListAdapter(mHearingDeviceItemList, this);
mDeviceList.setAdapter(mDeviceListAdapter);
}
private void setupPresetSpinner(SystemUIDialog dialog) {
- if (mPresetsController == null) {
- mPresetsController = new HearingDevicesPresetsController(mProfileManager,
- mPresetCallback);
- }
- final CachedBluetoothDevice activeHearingDevice = getActiveHearingDevice(
- mHearingDeviceItemList);
- mPresetsController.setHearingDeviceIfSupportHap(activeHearingDevice);
+ mPresetController = new HearingDevicesPresetsController(mProfileManager, mPresetCallback);
+ mPresetController.setDevice(getActiveHearingDevice());
mPresetInfoAdapter = new HearingDevicesSpinnerAdapter(dialog.getContext());
mPresetSpinner.setAdapter(mPresetInfoAdapter);
-
// disable redundant Touch & Hold accessibility action for Switch Access
mPresetSpinner.setAccessibilityDelegate(new View.AccessibilityDelegate() {
@Override
@@ -362,20 +330,18 @@
super.onInitializeAccessibilityNodeInfo(host, info);
}
});
-
- // Refresh the spinner and setSelection(index, false) before setOnItemSelectedListener() to
- // avoid extra onItemSelected() get called when first register the listener.
- final List<BluetoothHapPresetInfo> presetInfos = mPresetsController.getAllPresetInfo();
- final int activePresetIndex = mPresetsController.getActivePresetIndex();
- refreshPresetInfoAdapter(presetInfos, activePresetIndex);
+ // Should call setSelection(index, false) for the spinner before setOnItemSelectedListener()
+ // to avoid extra onItemSelected() get called when first register the listener.
+ refreshPresetUi(mPresetController.getAllPresetInfo(),
+ mPresetController.getActivePresetIndex());
mPresetSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
mPresetInfoAdapter.setSelected(position);
mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_PRESET_SELECT,
mLaunchSourceId);
- mPresetsController.selectPreset(
- mPresetsController.getAllPresetInfo().get(position).getIndex());
+ mPresetController.selectPreset(
+ mPresetController.getAllPresetInfo().get(position).getIndex());
}
@Override
@@ -383,9 +349,8 @@
// Do nothing
}
});
- mPresetLayout.setVisibility(
- (activeHearingDevice != null && activeHearingDevice.isConnectedHapClientDevice()
- && !mPresetInfoAdapter.isEmpty()) ? VISIBLE : GONE);
+
+ mPresetLayout.setVisibility(mPresetController.isPresetControlAvailable() ? VISIBLE : GONE);
}
private void setupPairNewDeviceButton(SystemUIDialog dialog) {
@@ -405,13 +370,12 @@
}
private void setupRelatedToolsView(SystemUIDialog dialog) {
-
final Context context = dialog.getContext();
final List<ToolItem> toolItemList = new ArrayList<>();
final String[] toolNameArray;
final String[] toolIconArray;
- ToolItem preInstalledItem = getLiveCaption(context);
+ ToolItem preInstalledItem = getLiveCaptionToolItem(context);
if (preInstalledItem != null) {
toolItemList.add(preInstalledItem);
}
@@ -432,7 +396,7 @@
final LinearLayout toolsContainer = dialog.requireViewById(R.id.tools_container);
for (int i = 0; i < toolItemList.size(); i++) {
- View view = createHearingToolView(context, toolItemList.get(i), toolsContainer);
+ View view = createToolView(context, toolItemList.get(i), toolsContainer);
toolsContainer.addView(view);
if (i != toolItemList.size() - 1) {
final int spaceSize = context.getResources().getDimensionPixelSize(
@@ -444,8 +408,14 @@
}
}
- private void refreshPresetInfoAdapter(List<BluetoothHapPresetInfo> presetInfos,
- int activePresetIndex) {
+ private void refreshDeviceUi() {
+ mHearingDeviceItemList = getHearingDeviceItemList();
+ mMainHandler.post(() -> {
+ mDeviceListAdapter.refreshDeviceItemList(mHearingDeviceItemList);
+ });
+ }
+
+ private void refreshPresetUi(List<BluetoothHapPresetInfo> presetInfos, int activePresetIndex) {
mPresetInfoAdapter.clear();
mPresetInfoAdapter.addAll(
presetInfos.stream().map(BluetoothHapPresetInfo::getName).toList());
@@ -460,12 +430,11 @@
}
}
- private List<DeviceItem> getHearingDevicesList() {
+ private List<DeviceItem> getHearingDeviceItemList() {
if (mLocalBluetoothManager == null
|| !mLocalBluetoothManager.getBluetoothAdapter().isEnabled()) {
return emptyList();
}
-
return mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy().stream()
.map(this::createHearingDeviceItem)
.filter(Objects::nonNull)
@@ -473,8 +442,8 @@
}
@Nullable
- private CachedBluetoothDevice getActiveHearingDevice(List<DeviceItem> hearingDeviceItemList) {
- return hearingDeviceItemList.stream()
+ private CachedBluetoothDevice getActiveHearingDevice() {
+ return mHearingDeviceItemList.stream()
.filter(item -> item.getType() == DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE)
.map(DeviceItem::getCachedBluetoothDevice)
.findFirst()
@@ -495,7 +464,7 @@
}
@NonNull
- private View createHearingToolView(Context context, ToolItem item, ViewGroup container) {
+ private View createToolView(Context context, ToolItem item, ViewGroup container) {
View view = LayoutInflater.from(context).inflate(R.layout.hearing_tool_item, container,
false);
ImageView icon = view.requireViewById(R.id.tool_icon);
@@ -522,7 +491,7 @@
return view;
}
- private ToolItem getLiveCaption(Context context) {
+ private ToolItem getLiveCaptionToolItem(Context context) {
final PackageManager packageManager = context.getPackageManager();
LIVE_CAPTION_INTENT.setPackage(packageManager.getSystemCaptionsServicePackageName());
final List<ResolveInfo> resolved = packageManager.queryIntentActivities(LIVE_CAPTION_INTENT,
@@ -534,7 +503,6 @@
LIVE_CAPTION_INTENT,
/* isCustomIcon= */ true);
}
-
return null;
}
@@ -544,7 +512,7 @@
}
}
- private void showPresetErrorToast(Context context) {
- Toast.makeText(context, R.string.hearing_devices_presets_error, Toast.LENGTH_SHORT).show();
+ private void showErrorToast(int stringResId) {
+ Toast.makeText(mDialog.getContext(), stringResId, Toast.LENGTH_SHORT).show();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
index aa95fd0..e109108 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
@@ -25,16 +25,18 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HapClientProfile;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.utils.ThreadUtils;
+import java.util.ArrayList;
import java.util.List;
/**
- * The controller of the hearing devices presets of the bluetooth Hearing Access Profile.
+ * The controller of handling hearing device preset with Bluetooth Hearing Access Profile(HAP).
*/
public class HearingDevicesPresetsController implements
LocalBluetoothProfileManager.ServiceListener, BluetoothHapClient.Callback {
@@ -46,11 +48,13 @@
private final HapClientProfile mHapClientProfile;
private final PresetCallback mPresetCallback;
- private CachedBluetoothDevice mActiveHearingDevice;
+ private CachedBluetoothDevice mDevice;
+ private List<BluetoothHapPresetInfo> mPresetInfos = new ArrayList<>();
+ private int mActivePresetIndex = BluetoothHapClient.PRESET_INDEX_UNAVAILABLE;
private int mSelectedPresetIndex;
- public HearingDevicesPresetsController(LocalBluetoothProfileManager profileManager,
- PresetCallback presetCallback) {
+ public HearingDevicesPresetsController(@NonNull LocalBluetoothProfileManager profileManager,
+ @Nullable PresetCallback presetCallback) {
mProfileManager = profileManager;
mHapClientProfile = mProfileManager.getHapClientProfile();
mPresetCallback = presetCallback;
@@ -61,7 +65,7 @@
if (mHapClientProfile != null && mHapClientProfile.isProfileReady()) {
mProfileManager.removeServiceListener(this);
registerHapCallback();
- mPresetCallback.onPresetInfoUpdated(getAllPresetInfo(), getActivePresetIndex());
+ refreshPresetInfo();
}
}
@@ -72,51 +76,53 @@
@Override
public void onPresetSelected(@NonNull BluetoothDevice device, int presetIndex, int reason) {
- if (mActiveHearingDevice == null) {
+ if (mDevice == null) {
return;
}
- if (device.equals(mActiveHearingDevice.getDevice())) {
+ if (device.equals(mDevice.getDevice())) {
if (DEBUG) {
Log.d(TAG, "onPresetSelected, device: " + device.getAddress()
+ ", presetIndex: " + presetIndex + ", reason: " + reason);
}
- mPresetCallback.onPresetInfoUpdated(getAllPresetInfo(), getActivePresetIndex());
+ refreshPresetInfo();
}
}
@Override
public void onPresetInfoChanged(@NonNull BluetoothDevice device,
@NonNull List<BluetoothHapPresetInfo> presetInfoList, int reason) {
- if (mActiveHearingDevice == null) {
+ if (mDevice == null) {
return;
}
- if (device.equals(mActiveHearingDevice.getDevice())) {
+ if (device.equals(mDevice.getDevice())) {
if (DEBUG) {
Log.d(TAG, "onPresetInfoChanged, device: " + device.getAddress()
+ ", reason: " + reason + ", infoList: " + presetInfoList);
}
- mPresetCallback.onPresetInfoUpdated(getAllPresetInfo(), getActivePresetIndex());
+ refreshPresetInfo();
}
}
@Override
public void onPresetSelectionFailed(@NonNull BluetoothDevice device, int reason) {
- if (mActiveHearingDevice == null) {
+ if (mDevice == null) {
return;
}
- if (device.equals(mActiveHearingDevice.getDevice())) {
+ if (device.equals(mDevice.getDevice())) {
Log.w(TAG, "onPresetSelectionFailed, device: " + device.getAddress()
+ ", reason: " + reason);
- mPresetCallback.onPresetCommandFailed(reason);
+ if (mPresetCallback != null) {
+ mPresetCallback.onPresetCommandFailed(reason);
+ }
}
}
@Override
public void onPresetSelectionForGroupFailed(int hapGroupId, int reason) {
- if (mActiveHearingDevice == null || mHapClientProfile == null) {
+ if (mDevice == null || mHapClientProfile == null) {
return;
}
- if (hapGroupId == mHapClientProfile.getHapGroup(mActiveHearingDevice.getDevice())) {
+ if (hapGroupId == mHapClientProfile.getHapGroup(mDevice.getDevice())) {
Log.w(TAG, "onPresetSelectionForGroupFailed, group: " + hapGroupId
+ ", reason: " + reason);
selectPresetIndependently(mSelectedPresetIndex);
@@ -125,33 +131,43 @@
@Override
public void onSetPresetNameFailed(@NonNull BluetoothDevice device, int reason) {
- if (mActiveHearingDevice == null) {
+ if (mDevice == null) {
return;
}
- if (device.equals(mActiveHearingDevice.getDevice())) {
+ if (device.equals(mDevice.getDevice())) {
Log.w(TAG, "onSetPresetNameFailed, device: " + device.getAddress()
+ ", reason: " + reason);
- mPresetCallback.onPresetCommandFailed(reason);
+ if (mPresetCallback != null) {
+ mPresetCallback.onPresetCommandFailed(reason);
+ }
}
}
@Override
public void onSetPresetNameForGroupFailed(int hapGroupId, int reason) {
- if (mActiveHearingDevice == null || mHapClientProfile == null) {
+ if (mDevice == null || mHapClientProfile == null) {
return;
}
- if (hapGroupId == mHapClientProfile.getHapGroup(mActiveHearingDevice.getDevice())) {
+ if (hapGroupId == mHapClientProfile.getHapGroup(mDevice.getDevice())) {
Log.w(TAG, "onSetPresetNameForGroupFailed, group: " + hapGroupId
+ ", reason: " + reason);
}
- mPresetCallback.onPresetCommandFailed(reason);
+ if (mPresetCallback != null) {
+ mPresetCallback.onPresetCommandFailed(reason);
+ }
}
/**
- * Registers a callback to be notified about operation changed for {@link HapClientProfile}.
+ * Registers a callback to be notified about operation changed of {@link HapClientProfile}.
*/
public void registerHapCallback() {
if (mHapClientProfile != null) {
+ if (!mHapClientProfile.isProfileReady()) {
+ mProfileManager.addServiceListener(this);
+ Log.w(TAG, "Profile is not ready yet, the callback will be registered once the "
+ + "profile is ready.");
+ return;
+ }
try {
mHapClientProfile.registerCallback(ThreadUtils.getBackgroundExecutor(), this);
} catch (IllegalArgumentException e) {
@@ -163,9 +179,10 @@
}
/**
- * Removes a previously-added {@link HapClientProfile} callback.
+ * Removes a previously-added {@link HapClientProfile} callback if exist.
*/
public void unregisterHapCallback() {
+ mProfileManager.removeServiceListener(this);
if (mHapClientProfile != null) {
try {
mHapClientProfile.unregisterCallback(this);
@@ -177,39 +194,94 @@
}
/**
- * Sets the hearing device for this controller to control the preset if it supports
- * {@link HapClientProfile}.
+ * Sets the device for this controller to control the preset if it supports
+ * {@link HapClientProfile}, otherwise the device of this controller will be {@code null}.
*
- * @param activeHearingDevice the {@link CachedBluetoothDevice} need to be hearing aid device
- * and support {@link HapClientProfile}.
+ * @param device the {@link CachedBluetoothDevice} set to the controller
*/
- public void setHearingDeviceIfSupportHap(CachedBluetoothDevice activeHearingDevice) {
- if (mHapClientProfile == null || activeHearingDevice == null) {
- mActiveHearingDevice = null;
- return;
- }
- if (activeHearingDevice.getProfiles().stream().anyMatch(
+ public void setDevice(@Nullable CachedBluetoothDevice device) {
+ if (device != null && device.getProfiles().stream().anyMatch(
profile -> profile instanceof HapClientProfile)) {
- mActiveHearingDevice = activeHearingDevice;
+ mDevice = device;
} else {
- mActiveHearingDevice = null;
+ mDevice = null;
+ }
+ refreshPresetInfo();
+ }
+
+ /**
+ * Refreshes the preset info of {@code mDevice}. If the preset info list or the active preset
+ * index is updated, the {@link PresetCallback#onPresetInfoUpdated(List, int)} will be called
+ * to notify the change.
+ *
+ * <b>Note:</b> If {@code mDevice} is null, the cached preset info and active preset index will
+ * be reset to empty list and {@code BluetoothHapClient.PRESET_INDEX_UNAVAILABLE} respectively.
+ */
+ public void refreshPresetInfo() {
+ List<BluetoothHapPresetInfo> updatedInfos = new ArrayList<>();
+ int updatedActiveIndex = BluetoothHapClient.PRESET_INDEX_UNAVAILABLE;
+ if (mHapClientProfile != null && mDevice != null) {
+ updatedInfos = mHapClientProfile.getAllPresetInfo(mDevice.getDevice()).stream().filter(
+ BluetoothHapPresetInfo::isAvailable).toList();
+ updatedActiveIndex = mHapClientProfile.getActivePresetIndex(mDevice.getDevice());
+ }
+ final boolean infoUpdated = !mPresetInfos.equals(updatedInfos);
+ final boolean activeIndexUpdated = mActivePresetIndex != updatedActiveIndex;
+ mPresetInfos = updatedInfos;
+ mActivePresetIndex = updatedActiveIndex;
+ if (infoUpdated || activeIndexUpdated) {
+ if (mPresetCallback != null) {
+ mPresetCallback.onPresetInfoUpdated(mPresetInfos, mActivePresetIndex);
+ }
}
}
/**
- * Selects the currently active preset for {@code mActiveHearingDevice} individual device or
- * the device group according to whether it supports synchronized presets or not.
+ * @return if the preset control is available. The preset control is available only
+ * when the {@code mDevice} supports HAP and the retrieved preset info list is not empty.
+ */
+ public boolean isPresetControlAvailable() {
+ boolean deviceValid = mDevice != null && mDevice.isConnectedHapClientDevice();
+ boolean hasPreset = mPresetInfos != null && !mPresetInfos.isEmpty();
+ return deviceValid && hasPreset;
+ }
+
+ /**
+ * @return a list of {@link BluetoothHapPresetInfo} retrieved from {@code mDevice}
+ */
+ public List<BluetoothHapPresetInfo> getAllPresetInfo() {
+ if (mDevice == null || mHapClientProfile == null) {
+ return emptyList();
+ }
+ return mPresetInfos;
+ }
+
+ /**
+ * Gets the currently active preset of {@code mDevice}.
+ *
+ * @return active preset index
+ */
+ public int getActivePresetIndex() {
+ if (mDevice == null || mHapClientProfile == null) {
+ return BluetoothHapClient.PRESET_INDEX_UNAVAILABLE;
+ }
+ return mActivePresetIndex;
+ }
+
+ /**
+ * Selects the preset for {@code mDevice}. Performs individual or group operation according
+ * to whether the device supports synchronized presets feature or not.
*
* @param presetIndex an index of one of the available presets
*/
public void selectPreset(int presetIndex) {
- if (mActiveHearingDevice == null || mHapClientProfile == null) {
+ if (mDevice == null || mHapClientProfile == null) {
return;
}
mSelectedPresetIndex = presetIndex;
boolean supportSynchronizedPresets = mHapClientProfile.supportsSynchronizedPresets(
- mActiveHearingDevice.getDevice());
- int hapGroupId = mHapClientProfile.getHapGroup(mActiveHearingDevice.getDevice());
+ mDevice.getDevice());
+ int hapGroupId = mHapClientProfile.getHapGroup(mDevice.getDevice());
if (supportSynchronizedPresets) {
if (hapGroupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
selectPresetSynchronously(hapGroupId, presetIndex);
@@ -222,63 +294,37 @@
}
}
- /**
- * Gets all preset info for {@code mActiveHearingDevice} device.
- *
- * @return a list of all known preset info
- */
- public List<BluetoothHapPresetInfo> getAllPresetInfo() {
- if (mActiveHearingDevice == null || mHapClientProfile == null) {
- return emptyList();
- }
- return mHapClientProfile.getAllPresetInfo(mActiveHearingDevice.getDevice()).stream().filter(
- BluetoothHapPresetInfo::isAvailable).toList();
- }
-
- /**
- * Gets the currently active preset for {@code mActiveHearingDevice} device.
- *
- * @return active preset index
- */
- public int getActivePresetIndex() {
- if (mActiveHearingDevice == null || mHapClientProfile == null) {
- return BluetoothHapClient.PRESET_INDEX_UNAVAILABLE;
- }
- return mHapClientProfile.getActivePresetIndex(mActiveHearingDevice.getDevice());
- }
-
private void selectPresetSynchronously(int groupId, int presetIndex) {
- if (mActiveHearingDevice == null || mHapClientProfile == null) {
+ if (mDevice == null || mHapClientProfile == null) {
return;
}
if (DEBUG) {
Log.d(TAG, "selectPresetSynchronously"
+ ", presetIndex: " + presetIndex
+ ", groupId: " + groupId
- + ", device: " + mActiveHearingDevice.getAddress());
+ + ", device: " + mDevice.getAddress());
}
mHapClientProfile.selectPresetForGroup(groupId, presetIndex);
}
private void selectPresetIndependently(int presetIndex) {
- if (mActiveHearingDevice == null || mHapClientProfile == null) {
+ if (mDevice == null || mHapClientProfile == null) {
return;
}
if (DEBUG) {
Log.d(TAG, "selectPresetIndependently"
+ ", presetIndex: " + presetIndex
- + ", device: " + mActiveHearingDevice.getAddress());
+ + ", device: " + mDevice.getAddress());
}
- mHapClientProfile.selectPreset(mActiveHearingDevice.getDevice(), presetIndex);
- final CachedBluetoothDevice subDevice = mActiveHearingDevice.getSubDevice();
+ mHapClientProfile.selectPreset(mDevice.getDevice(), presetIndex);
+ final CachedBluetoothDevice subDevice = mDevice.getSubDevice();
if (subDevice != null) {
if (DEBUG) {
Log.d(TAG, "selectPreset for subDevice, device: " + subDevice);
}
mHapClientProfile.selectPreset(subDevice.getDevice(), presetIndex);
}
- for (final CachedBluetoothDevice memberDevice :
- mActiveHearingDevice.getMemberDevice()) {
+ for (final CachedBluetoothDevice memberDevice : mDevice.getMemberDevice()) {
if (DEBUG) {
Log.d(TAG, "selectPreset for memberDevice, device: " + memberDevice);
}
@@ -294,9 +340,8 @@
/**
* Called when preset info from {@link HapClientProfile} operation get updated.
*
- * @param presetInfos all preset info for {@code mActiveHearingDevice} device
- * @param activePresetIndex currently active preset index for {@code mActiveHearingDevice}
- * device
+ * @param presetInfos all preset info of {@code mDevice}
+ * @param activePresetIndex currently active preset index of {@code mDevice}
*/
void onPresetInfoUpdated(List<BluetoothHapPresetInfo> presetInfos, int activePresetIndex);
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
index d4e74d3..9e1b09c 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
@@ -162,11 +162,12 @@
void showIcon(@StatusIconType int iconType, boolean show, @Nullable String contentDescription) {
View icon = mStatusIcons.get(iconType);
- if (icon == null) {
- return;
- }
+ if (icon == null) return;
+
if (show && contentDescription != null) {
icon.setContentDescription(contentDescription);
+ icon.setFocusable(true);
+ icon.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
icon.setVisibility(show ? View.VISIBLE : View.GONE);
mSystemStatusViewGroup.setVisibility(areAnyStatusIconsVisible() ? View.VISIBLE : View.GONE);
@@ -174,9 +175,12 @@
void setExtraStatusBarItemViews(List<View> views) {
removeAllExtraStatusBarItemViews();
- views.forEach(view -> mExtraSystemStatusViewGroup.addView(view));
+ views.forEach(view -> {
+ view.setFocusable(true);
+ view.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ mExtraSystemStatusViewGroup.addView(view);
+ });
}
-
private View fetchStatusIconForResId(int resId) {
final View statusIcon = findViewById(resId);
return Objects.requireNonNull(statusIcon);
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index cbdb882..5cf4b4f 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -242,6 +242,7 @@
op.getUid(),
op.getPackageName(),
/* attributionTag= */ attributedOpEntry.getKey(),
+ Context.DEVICE_ID_DEFAULT,
/* active= */ true,
// AppOpsManager doesn't have a way to fetch attribution flags or
// chain ID given an op entry, so default them to none.
@@ -440,14 +441,14 @@
* Required to override, delegate to other. Should not be called.
*/
public void onOpActiveChanged(String op, int uid, String packageName, boolean active) {
- onOpActiveChanged(op, uid, packageName, null, active,
+ onOpActiveChanged(op, uid, packageName, null, Context.DEVICE_ID_DEFAULT, active,
AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
}
// Get active app ops, and check if their attributions are trusted
@Override
public void onOpActiveChanged(String op, int uid, String packageName, String attributionTag,
- boolean active, int attributionFlags, int attributionChainId) {
+ int virtualDeviceId, boolean active, int attributionFlags, int attributionChainId) {
int code = AppOpsManager.strOpToOp(op);
if (DEBUG) {
Log.w(TAG, String.format("onActiveChanged(%d,%d,%s,%s,%d,%d)", code, uid, packageName,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index f6b6655..b653711 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -27,7 +27,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AlertDialog;
-import android.app.KeyguardManager;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
@@ -320,16 +319,6 @@
mBiometricCallback = new BiometricCallback();
mMSDLPlayer = msdlPlayer;
- // Listener for when device locks from adaptive auth, dismiss prompt
- getContext().getSystemService(KeyguardManager.class).addKeyguardLockedStateListener(
- getContext().getMainExecutor(),
- isKeyguardLocked -> {
- if (isKeyguardLocked) {
- onStartedGoingToSleep();
- }
- }
- );
-
final BiometricModalities biometricModalities = new BiometricModalities(
Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds),
Utils.findFirstSensorProperties(faceProps, mConfig.mSensorIds));
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 4faf6ff..316849d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -27,6 +27,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityTaskManager;
+import android.app.KeyguardManager;
import android.app.TaskStackListener;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -737,6 +738,7 @@
@Background DelayableExecutor bgExecutor,
@NonNull UdfpsUtils udfpsUtils,
@NonNull VibratorHelper vibratorHelper,
+ @NonNull KeyguardManager keyguardManager,
Lazy<ViewCapture> daggerLazyViewCapture,
@NonNull MSDLPlayer msdlPlayer) {
mContext = context;
@@ -768,6 +770,15 @@
mPromptViewModelProvider = promptViewModelProvider;
mCredentialViewModelProvider = credentialViewModelProvider;
+ keyguardManager.addKeyguardLockedStateListener(
+ context.getMainExecutor(),
+ isKeyguardLocked -> {
+ if (isKeyguardLocked) {
+ closeDialog("Device lock");
+ }
+ }
+ );
+
mOrientationListener = new BiometricDisplayListener(
context,
mDisplayManager,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
index 9763295..8a5e011 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
@@ -29,6 +29,7 @@
import com.android.systemui.res.R
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import javax.inject.Inject
+import kotlin.math.max
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -39,7 +40,6 @@
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
-import kotlin.math.max
/** Encapsulates business logic for interacting with the UDFPS overlay. */
@SysUISingleton
@@ -55,10 +55,7 @@
private fun calculateIconSize(): Int {
val pixelPitch = context.resources.getFloat(R.dimen.pixel_pitch)
if (pixelPitch <= 0) {
- Log.e(
- "UdfpsOverlayInteractor",
- "invalid pixelPitch: $pixelPitch. Pixel pitch must be updated per device.",
- )
+ Log.e(TAG, "invalid pixelPitch: $pixelPitch. Pixel pitch must be updated per device.")
}
return (context.resources.getFloat(R.dimen.udfps_icon_size) / pixelPitch).toInt()
}
@@ -84,13 +81,15 @@
}
/** Sets whether Udfps overlay should handle touches */
- fun setHandleTouches(shouldHandle: Boolean = true) {
- if (authController.isUdfpsSupported && shouldHandle != _shouldHandleTouches.value) {
+ fun setHandleTouches(shouldHandle: Boolean) {
+ if (authController.isUdfpsSupported) {
fingerprintManager?.setIgnoreDisplayTouches(
requestId.value,
authController.udfpsProps!!.get(0).sensorId,
!shouldHandle,
)
+ } else {
+ Log.d(TAG, "setIgnoreDisplayTouches not set, UDFPS not supported")
}
_shouldHandleTouches.value = shouldHandle
}
@@ -123,12 +122,14 @@
// Padding between the fingerprint icon and its bounding box in pixels.
val iconPadding: Flow<Int> =
- udfpsOverlayParams.map { params ->
- val sensorWidth = params.nativeSensorBounds.right - params.nativeSensorBounds.left
- val nativePadding = (sensorWidth - iconSize) / 2
- // padding can be negative when udfpsOverlayParams has not been initialized yet.
- max(0, (nativePadding * params.scaleFactor).toInt())
- }.distinctUntilChanged()
+ udfpsOverlayParams
+ .map { params ->
+ val sensorWidth = params.nativeSensorBounds.right - params.nativeSensorBounds.left
+ val nativePadding = (sensorWidth - iconSize) / 2
+ // padding can be negative when udfpsOverlayParams has not been initialized yet.
+ max(0, (nativePadding * params.scaleFactor).toInt())
+ }
+ .distinctUntilChanged()
companion object {
private const val TAG = "UdfpsOverlayInteractor"
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
index b2d02ed..a31e61f 100644
--- a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
@@ -107,6 +107,7 @@
activityOptions.setDisallowEnterPictureInPictureWhileLaunching(true)
activityOptions.rotationAnimationHint =
WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS
+ intent.collectExtraIntentKeys()
try {
activityTaskManager.startActivityAsUser(
null,
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt b/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt
index 074b64e..69f4f6d 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt
@@ -74,3 +74,12 @@
/** Returns `true` if the tap gesture should be rejected */
fun isFalseTap(@Penalty penalty: Int): Boolean = manager.isFalseTap(penalty)
}
+
+inline fun FalsingInteractor.runIfNotFalseTap(
+ penalty: Int = FalsingManager.LOW_PENALTY,
+ action: () -> Unit,
+) {
+ if (!isFalseTap(penalty)) {
+ action()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index d648b9c..5644e6b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -23,7 +23,6 @@
import com.android.compose.animation.scene.TransitionKey
import com.android.internal.logging.UiEventLogger
import com.android.systemui.CoreStartable
-import com.android.systemui.Flags.communalHubOnMobile
import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
@@ -218,7 +217,8 @@
newScene = CommunalScenes.Blank,
loggingReason = "hub timeout",
transitionKey =
- if (communalHubOnMobile()) CommunalTransitionKeys.SimpleFade
+ if (communalSettingsInteractor.isV2FlagEnabled())
+ CommunalTransitionKeys.SimpleFade
else null,
)
uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_TIMEOUT)
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 f9b30c6..ea42869 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
@@ -27,6 +27,7 @@
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.TransitionKey
+import com.android.systemui.Flags.communalResponsiveGrid
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.communal.data.repository.CommunalMediaRepository
import com.android.systemui.communal.data.repository.CommunalSmartspaceRepository
@@ -535,7 +536,9 @@
// Order by creation time descending.
ongoingContent.sortByDescending { it.createdTimestampMillis }
// Resize the items.
- ongoingContent.resizeItems()
+ if (!communalResponsiveGrid()) {
+ ongoingContent.resizeItems()
+ }
// Return the sorted and resized items.
ongoingContent
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 0cdbad4..862b05b 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
@@ -63,24 +63,41 @@
.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
columnPrefix = "disabledReason",
- initialValue = CommunalEnabledState()
+ initialValue = CommunalEnabledState(),
)
.map { model -> model.enabled }
// Start this eagerly since the value is accessed synchronously in many places.
.stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)
/**
- * Returns true if both the communal trunk-stable flag and resource flag are enabled.
+ * Returns true if any glanceable hub functionality should be enabled via configs and flags.
*
- * The trunk-stable flag is controlled by server rollout and is on all devices. The resource
- * flag is enabled via resource overlay only on products we want the hub to be present on.
+ * This should be used for preventing basic glanceable hub functionality from running on devices
+ * that don't need it.
*
* If this is false, then the hub is definitely not available on the device. If this is true,
* refer to [isCommunalEnabled] which takes into account other factors that can change at
* runtime.
+ *
+ * If the glanceable_hub_v2 flag is enabled, checks the config_glanceableHubEnabled Android
+ * config boolean. Otherwise, checks the old config_communalServiceEnabled config and
+ * communal_hub flag.
*/
fun isCommunalFlagEnabled(): Boolean = repository.getFlagEnabled()
+ /**
+ * Returns true if the Android config config_glanceableHubEnabled and the glanceable_hub_v2 flag
+ * are enabled.
+ *
+ * This should be used to flag off new glanceable hub or dream behavior that should launch
+ * together with the new hub experience that brings the hub to mobile.
+ *
+ * The trunk-stable flag is controlled by server rollout and is on all devices. The Android
+ * config flag is enabled via resource overlay only on products we want the hub to be present
+ * on.
+ */
+ fun isV2FlagEnabled(): Boolean = repository.getV2FlagEnabled()
+
/** The type of background to use for the hub. Used to experiment with different backgrounds */
val communalBackground: Flow<CommunalBackgroundType> =
userInteractor.selectedUserInfo
@@ -120,6 +137,6 @@
.stateIn(
scope = bgScope,
started = SharingStarted.WhileSubscribed(),
- initialValue = null
+ initialValue = null,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
index da613f5..c0456d5 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
@@ -110,6 +110,11 @@
get() = fixedHalfOrResponsiveSize()
}
+ /** An empty spacer to reserve space in the grid. */
+ data class Spacer(override val size: CommunalContentSize) : CommunalContentModel {
+ override val key: String = KEY.spacer()
+ }
+
/** A CTA tile in the glanceable hub view mode which can be dismissed. */
class CtaTileInViewMode : CommunalContentModel {
override val key: String = KEY.CTA_TILE_IN_VIEW_MODE_KEY
@@ -171,6 +176,10 @@
fun umo(): String {
return "umo"
}
+
+ fun spacer(): String {
+ return "spacer_${UUID.randomUUID()}"
+ }
}
}
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 a339af3..5ecf2e6b 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
@@ -195,7 +195,7 @@
open fun onDismissCtaTile() {}
/** Called as the user starts dragging a widget to reorder. */
- open fun onReorderWidgetStart(draggingItemKey: String) {}
+ open fun onReorderWidgetStart() {}
/** Called as the user finishes dragging a widget to reorder. */
open fun onReorderWidgetEnd() {}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index 52bf000..736ed5c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -164,8 +164,9 @@
)
}
- override fun onReorderWidgetStart(draggingItemKey: String) {
- setSelectedKey(draggingItemKey)
+ override fun onReorderWidgetStart() {
+ // Clear selection status
+ setSelectedKey(null)
_reorderingWidgets.value = true
uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_START)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
new file mode 100644
index 0000000..7d5b196
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
@@ -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 com.android.systemui.communal.ui.viewmodel
+
+import android.annotation.SuppressLint
+import android.app.DreamManager
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.util.kotlin.isDevicePluggedIn
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+class CommunalToDreamButtonViewModel
+@AssistedInject
+constructor(
+ @Background private val backgroundContext: CoroutineContext,
+ batteryController: BatteryController,
+ private val dreamManager: DreamManager,
+) : ExclusiveActivatable() {
+
+ private val _requests = Channel<Unit>(Channel.BUFFERED)
+
+ /** Whether we should show a button on hub to switch to dream. */
+ @SuppressLint("MissingPermission")
+ val shouldShowDreamButtonOnHub =
+ batteryController
+ .isDevicePluggedIn()
+ .distinctUntilChanged()
+ .map { isPluggedIn -> isPluggedIn && dreamManager.canStartDreaming(true) }
+ .flowOn(backgroundContext)
+
+ /** Handle a tap on the "show dream" button. */
+ fun onShowDreamButtonTap() {
+ _requests.trySend(Unit)
+ }
+
+ @SuppressLint("MissingPermission")
+ override suspend fun onActivated(): Nothing = coroutineScope {
+ launch {
+ _requests.receiveAsFlow().collectLatest {
+ withContext(backgroundContext) { dreamManager.startDream() }
+ }
+ }
+
+ awaitCancellation()
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): CommunalToDreamButtonViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 9cd6465..eb7420f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -90,7 +90,7 @@
private val keyguardIndicationController: KeyguardIndicationController,
communalSceneInteractor: CommunalSceneInteractor,
private val communalInteractor: CommunalInteractor,
- communalSettingsInteractor: CommunalSettingsInteractor,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
tutorialInteractor: CommunalTutorialInteractor,
private val shadeInteractor: ShadeInteractor,
@Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
@@ -372,6 +372,9 @@
val communalBackground: Flow<CommunalBackgroundType> =
communalSettingsInteractor.communalBackground
+ /** See [CommunalSettingsInteractor.isV2FlagEnabled] */
+ fun v2FlagEnabled(): Boolean = communalSettingsInteractor.isV2FlagEnabled()
+
companion object {
const val POPUP_AUTO_HIDE_TIMEOUT_MS = 12000L
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/ResizeUtils.kt b/packages/SystemUI/src/com/android/systemui/communal/util/ResizeUtils.kt
new file mode 100644
index 0000000..36cc1c0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/util/ResizeUtils.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.model.CommunalContentSize
+
+object ResizeUtils {
+ /**
+ * Resizes ongoing items such that we don't mix regular content with ongoing content.
+ *
+ * NOTE: This is *NOT* a pure function, as it modifies items in the input list.
+ *
+ * Assumptions:
+ * 1. Ongoing content is always at the start of the list.
+ * 2. The maximum size of ongoing content is 2 rows.
+ */
+ fun resizeOngoingItems(
+ list: List<CommunalContentModel>,
+ numRows: Int,
+ ): List<CommunalContentModel> {
+ val finalizedList = mutableListOf<CommunalContentModel>()
+ val numOngoing = list.count { it is CommunalContentModel.Ongoing }
+ // Calculate the number of extra rows we have if each ongoing item were to take up a single
+ // row. This is the number of rows we have to distribute across items.
+ var extraRows =
+ if (numOngoing % numRows == 0) {
+ 0
+ } else {
+ numRows - (numOngoing % numRows)
+ }
+ var remainingRows = numRows
+
+ for (item in list) {
+ if (item is CommunalContentModel.Ongoing) {
+ if (remainingRows == 0) {
+ // Start a new column.
+ remainingRows = numRows
+ }
+ val newSize = if (extraRows > 0 && remainingRows > 1) 2 else 1
+ item.size = CommunalContentSize.Responsive(newSize)
+ finalizedList.add(item)
+ extraRows -= (newSize - 1)
+ remainingRows -= newSize
+ } else {
+ if (numOngoing > 0 && remainingRows > 0) {
+ finalizedList.add(
+ CommunalContentModel.Spacer(CommunalContentSize.Responsive(remainingRows))
+ )
+ }
+ remainingRows = -1
+ finalizedList.add(item)
+ }
+ }
+ return finalizedList
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
index e78ce3b..f804c2e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
@@ -24,6 +24,7 @@
import com.android.systemui.flags.SystemPropertiesHelper;
import com.android.systemui.process.ProcessWrapper;
import com.android.systemui.util.InitializationChecker;
+import com.android.wm.shell.dagger.WMComponent;
import dagger.BindsInstance;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.kt
index e76fd47..c425bee 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.kt
@@ -25,7 +25,7 @@
import android.os.RemoteException
import android.service.dreams.IDreamManager
import android.util.Log
-import com.android.systemui.Flags
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.dagger.qualifiers.SystemUser
import com.android.systemui.dreams.dagger.DreamModule
import com.android.systemui.log.LogBuffer
@@ -48,6 +48,7 @@
@SystemUser monitor: Monitor,
private val packageManager: PackageManager,
private val dreamManager: IDreamManager,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
@DreamLog private val logBuffer: LogBuffer,
) : ConditionalCoreStartable(monitor) {
private var currentRegisteredState = false
@@ -90,7 +91,7 @@
}
if (
- Flags.communalHubOnMobile() &&
+ communalSettingsInteractor.isV2FlagEnabled() &&
packageManager.getComponentEnabledSetting(overlayServiceComponent) ==
PackageManager.COMPONENT_ENABLED_STATE_ENABLED
) {
@@ -111,7 +112,7 @@
}
// Enable for hub on mobile
- if (Flags.communalHubOnMobile()) {
+ if (communalSettingsInteractor.isV2FlagEnabled()) {
// Not available on TV or auto
if (
packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) ||
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index aee3a45..571b37f 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -18,7 +18,6 @@
import static android.service.dreams.Flags.dreamWakeRedirect;
-import static com.android.systemui.Flags.communalHubOnMobile;
import static com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming;
import static com.android.systemui.dreams.dagger.DreamModule.DREAM_OVERLAY_WINDOW_TITLE;
import static com.android.systemui.dreams.dagger.DreamModule.DREAM_TOUCH_INSET_MANAGER;
@@ -60,6 +59,7 @@
import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent;
import com.android.systemui.ambient.touch.scrim.ScrimManager;
import com.android.systemui.communal.domain.interactor.CommunalInteractor;
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor;
import com.android.systemui.communal.shared.log.CommunalUiEvent;
import com.android.systemui.communal.shared.model.CommunalScenes;
import com.android.systemui.communal.shared.model.CommunalTransitionKeys;
@@ -171,6 +171,7 @@
private final SceneInteractor mSceneInteractor;
private final CommunalInteractor mCommunalInteractor;
+ private final CommunalSettingsInteractor mCommunalSettingsInteractor;
private boolean mCommunalAvailable;
@@ -383,6 +384,7 @@
KeyguardUpdateMonitor keyguardUpdateMonitor,
ScrimManager scrimManager,
CommunalInteractor communalInteractor,
+ CommunalSettingsInteractor communalSettingsInteractor,
SceneInteractor sceneInteractor,
SystemDialogsCloser systemDialogsCloser,
UiEventLogger uiEventLogger,
@@ -411,6 +413,7 @@
mDreamOverlayCallbackController = dreamOverlayCallbackController;
mWindowTitle = windowTitle;
mCommunalInteractor = communalInteractor;
+ mCommunalSettingsInteractor = communalSettingsInteractor;
mSceneInteractor = sceneInteractor;
mSystemDialogsCloser = systemDialogsCloser;
mGestureInteractor = gestureInteractor;
@@ -488,7 +491,7 @@
final ArrayList<TouchHandler> touchHandlers = new ArrayList<>(
List.of(dreamComplicationComponent.getHideComplicationTouchHandler()));
- if (!communalHubOnMobile()) {
+ if (!mCommunalSettingsInteractor.isV2FlagEnabled()) {
// Do not add the communal touch handler for glanceable hub v2 since there is no dream
// to hub swipe gesture.
touchHandlers.add(dreamOverlayComponent.getCommunalTouchHandler());
@@ -575,7 +578,8 @@
} else {
mCommunalInteractor.changeScene(CommunalScenes.Communal,
"dream wake requested",
- communalHubOnMobile() ? CommunalTransitionKeys.INSTANCE.getSimpleFade() : null);
+ mCommunalSettingsInteractor.isV2FlagEnabled()
+ ? CommunalTransitionKeys.INSTANCE.getSimpleFade() : null);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
index 7242770..e264635 100644
--- a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
@@ -21,6 +21,9 @@
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.contextualeducation.GestureType
import com.android.systemui.contextualeducation.GestureType.ALL_APPS
+import com.android.systemui.contextualeducation.GestureType.BACK
+import com.android.systemui.contextualeducation.GestureType.HOME
+import com.android.systemui.contextualeducation.GestureType.OVERVIEW
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.education.ContextualEducationMetricsLogger
@@ -37,6 +40,7 @@
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import java.time.Clock
+import java.time.Instant
import javax.inject.Inject
import kotlin.time.Duration
import kotlin.time.Duration.Companion.days
@@ -48,6 +52,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.merge
@@ -71,6 +76,8 @@
const val TAG = "KeyboardTouchpadEduInteractor"
const val MAX_SIGNAL_COUNT: Int = 2
const val MAX_EDUCATION_SHOW_COUNT: Int = 2
+ const val MAX_TOAST_PER_USAGE_SESSION: Int = 2
+
val usageSessionDuration =
getDurationForConfig("persist.contextual_edu.usage_session_sec", 3.days)
val minIntervalBetweenEdu =
@@ -110,6 +117,16 @@
awaitClose { overviewProxyService.removeCallback(listener) }
}
+ private val gestureModelMap: Flow<Map<GestureType, GestureEduModel>> =
+ combine(
+ contextualEducationInteractor.backGestureModelFlow,
+ contextualEducationInteractor.homeGestureModelFlow,
+ contextualEducationInteractor.overviewGestureModelFlow,
+ contextualEducationInteractor.allAppsGestureModelFlow,
+ ) { back, home, overview, allApps ->
+ mapOf(BACK to back, HOME to home, OVERVIEW to overview, ALL_APPS to allApps)
+ }
+
@OptIn(ExperimentalCoroutinesApi::class)
override fun start() {
backgroundScope.launch {
@@ -211,7 +228,11 @@
private suspend fun incrementSignalCount(gestureType: GestureType) {
val targetDevice = getTargetDevice(gestureType)
- if (isTargetDeviceConnected(targetDevice) && hasInitialDelayElapsed(targetDevice)) {
+ if (
+ isTargetDeviceConnected(targetDevice) &&
+ hasInitialDelayElapsed(targetDevice) &&
+ isMinIntervalForToastEduElapsed(gestureType)
+ ) {
contextualEducationInteractor.incrementSignalCount(gestureType)
}
}
@@ -223,6 +244,28 @@
}
}
+ private suspend fun isMinIntervalForToastEduElapsed(gestureType: GestureType): Boolean {
+ val gestureModelMap = gestureModelMap.first()
+ // Only perform checking if the next edu is toast (i.e. no education is shown yet)
+ if (gestureModelMap[gestureType]?.educationShownCount != 0) {
+ return true
+ }
+
+ val wasLastEduToast = { gesture: GestureEduModel -> gesture.educationShownCount == 1 }
+ val toastEduTimesInCurrentSession: List<Instant> =
+ gestureModelMap.values
+ .filter { wasLastEduToast(it) }
+ .mapNotNull { it.lastEducationTime }
+ .filter { it >= clock.instant().minusSeconds(usageSessionDuration.inWholeSeconds) }
+
+ return if (toastEduTimesInCurrentSession.size >= MAX_TOAST_PER_USAGE_SESSION) {
+ val lastToastTime: Instant? = toastEduTimesInCurrentSession.maxOrNull()
+ clock.instant().isAfter(lastToastTime?.plusSeconds(usageSessionDuration.inWholeSeconds))
+ } else {
+ true
+ }
+ }
+
/**
* Keyboard shortcut education would be provided for All Apps. Touchpad gesture education would
* be provided for the rest of the gesture types (i.e. Home, Overview, Back). This method maps
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
index 1b044de..0c1bc83 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
@@ -20,6 +20,7 @@
import androidx.annotation.RawRes
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background
+import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -33,8 +34,12 @@
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalConfiguration
@@ -125,6 +130,8 @@
config: TutorialScreenConfig,
modifier: Modifier = Modifier,
) {
+ val focusRequester = remember { FocusRequester() }
+ LaunchedEffect(Unit) { focusRequester.requestFocus() }
val (titleTextId, bodyTextId) =
if (actionState is Finished) {
config.strings.titleSuccessResId to config.strings.bodySuccessResId
@@ -136,6 +143,7 @@
text = stringResource(id = titleTextId),
style = MaterialTheme.typography.displayLarge,
color = config.colors.title,
+ modifier = Modifier.focusRequester(focusRequester).focusable(),
)
Spacer(modifier = Modifier.height(16.dp))
Text(
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt
index abd39cc..ad18817 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt
@@ -36,6 +36,9 @@
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.node.Ref
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.util.lerp
import com.airbnb.lottie.LottieComposition
import com.airbnb.lottie.compose.LottieAnimation
@@ -47,6 +50,7 @@
import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished
import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress
import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted
+import com.android.systemui.res.R
@Composable
fun TutorialAnimation(
@@ -104,11 +108,15 @@
isPlaying = isPlaying,
restartOnPlay = false,
)
+ val animationDescription = stringResource(R.string.tutorial_animation_content_description)
LottieAnimation(
composition = composition,
progress = { progress },
dynamicProperties = animationProperties,
- modifier = Modifier.fillMaxSize().clickable { isPlaying = !isPlaying },
+ modifier =
+ Modifier.fillMaxSize()
+ .clickable { isPlaying = !isPlaying }
+ .semantics { contentDescription = animationDescription },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/model/InternalShortcutModels.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/model/InternalShortcutModels.kt
index 8c393e2..3020e5d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/model/InternalShortcutModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/model/InternalShortcutModels.kt
@@ -21,6 +21,7 @@
import android.view.KeyboardShortcutGroup
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutCategoriesUtils
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
/**
* Internal Keyboard Shortcut models to use with [ShortcutCategoriesUtils.fetchShortcutCategory]
@@ -55,3 +56,8 @@
val icon: Icon? = null,
val isCustomShortcut: Boolean = false,
)
+
+data class InternalGroupsSource(
+ val groups: List<InternalKeyboardShortcutGroup>,
+ val type: ShortcutCategoryType,
+)
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
index 4af3786..8afec04 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
@@ -16,10 +16,8 @@
package com.android.systemui.keyboard.shortcut.data.repository
-import android.content.Context
import android.hardware.input.InputGestureData
import android.hardware.input.InputGestureData.Builder
-import android.hardware.input.InputGestureData.KeyTrigger
import android.hardware.input.InputGestureData.createKeyTrigger
import android.hardware.input.InputManager
import android.hardware.input.KeyGestureEvent.KeyGestureType
@@ -30,11 +28,8 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult
-import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutGroup
-import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutInfo
import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
@@ -57,8 +52,7 @@
@Background private val backgroundScope: CoroutineScope,
@Background private val bgCoroutineContext: CoroutineContext,
private val shortcutCategoriesUtils: ShortcutCategoriesUtils,
- private val context: Context,
- private val inputGestureMaps: InputGestureMaps,
+ private val inputGestureDataAdapter: InputGestureDataAdapter,
private val customInputGesturesRepository: CustomInputGesturesRepository,
private val inputManager: InputManager
) : ShortcutCategoriesRepository {
@@ -116,7 +110,7 @@
if (inputDevice == null) {
emptyList()
} else {
- val sources = toInternalGroupSources(inputGestures)
+ val sources = inputGestureDataAdapter.toInternalGroupSources(inputGestures)
val supportedKeyCodes =
shortcutCategoriesUtils.fetchSupportedKeyCodes(
inputDevice.id,
@@ -216,7 +210,8 @@
return null
}
- return inputGestureMaps.shortcutLabelToKeyGestureTypeMap[shortcutBeingCustomized.label]
+ return inputGestureDataAdapter
+ .getKeyGestureTypeFromShortcutLabel(shortcutBeingCustomized.label)
}
@KeyGestureType
@@ -232,7 +227,8 @@
return null
}
- return inputGestureMaps.shortcutLabelToKeyGestureTypeMap[shortcutBeingCustomized.label]
+ return inputGestureDataAdapter
+ .getKeyGestureTypeFromShortcutLabel(shortcutBeingCustomized.label)
}
private fun Builder.addTriggerFromSelectedKeyCombination(): Builder {
@@ -261,70 +257,6 @@
return _shortcutBeingCustomized.value
}
- private fun toInternalGroupSources(
- inputGestures: List<InputGestureData>
- ): List<InternalGroupsSource> {
- val ungroupedInternalGroupSources =
- inputGestures.mapNotNull { gestureData ->
- val keyTrigger = gestureData.trigger as KeyTrigger
- val keyGestureType = gestureData.action.keyGestureType()
- fetchGroupLabelByGestureType(keyGestureType)?.let { groupLabel ->
- toInternalKeyboardShortcutInfo(keyGestureType, keyTrigger)?.let {
- internalKeyboardShortcutInfo ->
- val group =
- InternalKeyboardShortcutGroup(
- label = groupLabel,
- items = listOf(internalKeyboardShortcutInfo),
- )
-
- fetchShortcutCategoryTypeByGestureType(keyGestureType)?.let {
- InternalGroupsSource(groups = listOf(group), type = it)
- }
- }
- }
- }
-
- return ungroupedInternalGroupSources
- }
-
- private fun toInternalKeyboardShortcutInfo(
- keyGestureType: Int,
- keyTrigger: KeyTrigger,
- ): InternalKeyboardShortcutInfo? {
- fetchShortcutInfoLabelByGestureType(keyGestureType)?.let {
- return InternalKeyboardShortcutInfo(
- label = it,
- keycode = keyTrigger.keycode,
- modifiers = keyTrigger.modifierState,
- isCustomShortcut = true,
- )
- }
- return null
- }
-
- private fun fetchGroupLabelByGestureType(@KeyGestureType keyGestureType: Int): String? {
- inputGestureMaps.gestureToInternalKeyboardShortcutGroupLabelResIdMap[keyGestureType]?.let {
- return context.getString(it)
- } ?: return null
- }
-
- private fun fetchShortcutInfoLabelByGestureType(@KeyGestureType keyGestureType: Int): String? {
- inputGestureMaps.gestureToInternalKeyboardShortcutInfoLabelResIdMap[keyGestureType]?.let {
- return context.getString(it)
- } ?: return null
- }
-
- private fun fetchShortcutCategoryTypeByGestureType(
- @KeyGestureType keyGestureType: Int
- ): ShortcutCategoryType? {
- return inputGestureMaps.gestureToShortcutCategoryTypeMap[keyGestureType]
- }
-
- private data class InternalGroupsSource(
- val groups: List<InternalKeyboardShortcutGroup>,
- val type: ShortcutCategoryType,
- )
-
private companion object {
private const val TAG = "CustomShortcutCategoriesRepository"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapter.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapter.kt
new file mode 100644
index 0000000..df7101e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureDataAdapter.kt
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.keyboard.shortcut.data.repository
+
+import android.annotation.SuppressLint
+import android.app.role.RoleManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.Intent.ACTION_MAIN
+import android.content.Intent.CATEGORY_LAUNCHER
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager.MATCH_DEFAULT_ONLY
+import android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE
+import android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+import android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES
+import android.content.pm.PackageManager.NameNotFoundException
+import android.graphics.drawable.Icon
+import android.hardware.input.AppLaunchData
+import android.hardware.input.AppLaunchData.CategoryData
+import android.hardware.input.AppLaunchData.ComponentData
+import android.hardware.input.AppLaunchData.RoleData
+import android.hardware.input.InputGestureData
+import android.hardware.input.InputGestureData.KeyTrigger
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION
+import android.hardware.input.KeyGestureEvent.KeyGestureType
+import android.util.Log
+import com.android.internal.app.ResolverActivity
+import com.android.systemui.keyboard.shortcut.data.model.InternalGroupsSource
+import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutGroup
+import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutInfo
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
+import com.android.systemui.res.R
+import com.android.systemui.settings.UserTracker
+import javax.inject.Inject
+
+
+class InputGestureDataAdapter
+@Inject
+constructor(
+ private val userTracker: UserTracker,
+ private val inputGestureMaps: InputGestureMaps,
+ private val context: Context
+) {
+ private val userContext: Context
+ get() = userTracker.createCurrentUserContext(userTracker.userContext)
+
+ fun toInternalGroupSources(
+ inputGestures: List<InputGestureData>
+ ): List<InternalGroupsSource> {
+ val ungroupedInternalGroupSources =
+ inputGestures.mapNotNull { gestureData ->
+ val keyTrigger = gestureData.trigger as KeyTrigger
+ val keyGestureType = gestureData.action.keyGestureType()
+ val appLaunchData: AppLaunchData? = gestureData.action.appLaunchData()
+ fetchGroupLabelByGestureType(keyGestureType)?.let { groupLabel ->
+ toInternalKeyboardShortcutInfo(
+ keyGestureType,
+ keyTrigger,
+ appLaunchData
+ )?.let { internalKeyboardShortcutInfo ->
+ val group =
+ InternalKeyboardShortcutGroup(
+ label = groupLabel,
+ items = listOf(internalKeyboardShortcutInfo),
+ )
+
+ fetchShortcutCategoryTypeByGestureType(keyGestureType)?.let {
+ InternalGroupsSource(groups = listOf(group), type = it)
+ }
+ }
+ }
+ }
+
+ return ungroupedInternalGroupSources
+ }
+
+ fun getKeyGestureTypeFromShortcutLabel(label: String): Int? {
+ return inputGestureMaps.shortcutLabelToKeyGestureTypeMap[label]
+ }
+
+ private fun toInternalKeyboardShortcutInfo(
+ keyGestureType: Int,
+ keyTrigger: KeyTrigger,
+ appLaunchData: AppLaunchData?,
+ ): InternalKeyboardShortcutInfo? {
+ fetchShortcutLabelByGestureType(keyGestureType, appLaunchData)?.let {
+ return InternalKeyboardShortcutInfo(
+ label = it,
+ keycode = keyTrigger.keycode,
+ modifiers = keyTrigger.modifierState,
+ isCustomShortcut = true,
+ icon = appLaunchData?.let { fetchShortcutIconByAppLaunchData(appLaunchData) }
+ )
+ }
+ return null
+ }
+
+ @SuppressLint("QueryPermissionsNeeded")
+ private fun fetchShortcutIconByAppLaunchData(
+ appLaunchData: AppLaunchData
+ ): Icon? {
+ val intent = fetchIntentFromAppLaunchData(appLaunchData) ?: return null
+ val resolvedActivity = resolveSingleMatchingActivityFrom(intent)
+
+ return if (resolvedActivity == null) {
+ null
+ } else {
+ Icon.createWithResource(context, resolvedActivity.iconResource)
+ }
+ }
+
+ private fun fetchGroupLabelByGestureType(@KeyGestureType keyGestureType: Int): String? {
+ inputGestureMaps.gestureToInternalKeyboardShortcutGroupLabelResIdMap[keyGestureType]?.let {
+ return context.getString(it)
+ } ?: return null
+ }
+
+ private fun fetchShortcutLabelByGestureType(
+ @KeyGestureType keyGestureType: Int,
+ appLaunchData: AppLaunchData?
+ ): String? {
+ inputGestureMaps.gestureToInternalKeyboardShortcutInfoLabelResIdMap[keyGestureType]?.let {
+ return context.getString(it)
+ }
+
+ if (keyGestureType == KEY_GESTURE_TYPE_LAUNCH_APPLICATION) {
+ return fetchShortcutLabelByAppLaunchData(appLaunchData!!)
+ }
+
+ return null
+ }
+
+ private fun fetchShortcutLabelByAppLaunchData(appLaunchData: AppLaunchData): String? {
+ val intent = fetchIntentFromAppLaunchData(appLaunchData) ?: return null
+ val resolvedActivity = resolveSingleMatchingActivityFrom(intent)
+
+ return if (resolvedActivity == null) {
+ getIntentCategoryLabel(intent.selector?.categories?.iterator()?.next())
+ } else resolvedActivity.loadLabel(userContext.packageManager).toString()
+
+ }
+
+ @SuppressLint("QueryPermissionsNeeded")
+ private fun resolveSingleMatchingActivityFrom(intent: Intent): ActivityInfo? {
+ val packageManager = userContext.packageManager
+ val resolvedActivity = intent.resolveActivityInfo(
+ packageManager,
+ /* flags= */ MATCH_DEFAULT_ONLY
+ ) ?: return null
+
+ val matchesMultipleActivities =
+ ResolverActivity::class.qualifiedName.equals(resolvedActivity.name)
+
+ return if (matchesMultipleActivities) {
+ return null
+ } else resolvedActivity
+ }
+
+ private fun getIntentCategoryLabel(category: String?): String? {
+ val categoryLabelRes = when (category.toString()) {
+ Intent.CATEGORY_APP_BROWSER -> R.string.keyboard_shortcut_group_applications_browser
+ Intent.CATEGORY_APP_CONTACTS -> R.string.keyboard_shortcut_group_applications_contacts
+ Intent.CATEGORY_APP_EMAIL -> R.string.keyboard_shortcut_group_applications_email
+ Intent.CATEGORY_APP_CALENDAR -> R.string.keyboard_shortcut_group_applications_calendar
+ Intent.CATEGORY_APP_MAPS -> R.string.keyboard_shortcut_group_applications_maps
+ Intent.CATEGORY_APP_MUSIC -> R.string.keyboard_shortcut_group_applications_music
+ Intent.CATEGORY_APP_MESSAGING -> R.string.keyboard_shortcut_group_applications_sms
+ Intent.CATEGORY_APP_CALCULATOR -> R.string.keyboard_shortcut_group_applications_calculator
+ else -> {
+ Log.w(TAG, ("No label for app category $category"))
+ null
+ }
+ }
+
+ return if (categoryLabelRes == null){
+ return null
+ } else {
+ context.getString(categoryLabelRes)
+ }
+ }
+
+ private fun fetchIntentFromAppLaunchData(appLaunchData: AppLaunchData): Intent? {
+ return when (appLaunchData) {
+ is CategoryData -> Intent.makeMainSelectorActivity(
+ /* selectorAction= */ ACTION_MAIN,
+ /* selectorCategory= */ appLaunchData.category
+ )
+
+ is RoleData -> getRoleLaunchIntent(appLaunchData.role)
+ is ComponentData -> resolveComponentNameIntent(
+ packageName = appLaunchData.packageName,
+ className = appLaunchData.className
+ )
+
+ else -> null
+ }
+ }
+
+ private fun resolveComponentNameIntent(packageName: String, className: String): Intent? {
+ buildIntentFromComponentName(ComponentName(packageName, className))?.let { return it }
+ buildIntentFromComponentName(ComponentName(
+ userContext.packageManager.canonicalToCurrentPackageNames(arrayOf(packageName))[0],
+ className
+ ))?.let { return it }
+ return null
+ }
+
+ private fun buildIntentFromComponentName(componentName: ComponentName): Intent? {
+ try{
+ val flags =
+ MATCH_DIRECT_BOOT_UNAWARE or MATCH_DIRECT_BOOT_AWARE or MATCH_UNINSTALLED_PACKAGES
+ // attempt to retrieve activity info to see if a NameNotFoundException is thrown.
+ userContext.packageManager.getActivityInfo(componentName, flags)
+ } catch (e: NameNotFoundException) {
+ Log.w(
+ TAG,
+ "Unable to find activity info for componentName: $componentName"
+ )
+ return null
+ }
+
+ return Intent(ACTION_MAIN).apply {
+ addCategory(CATEGORY_LAUNCHER)
+ component = componentName
+ }
+ }
+
+ @SuppressLint("NonInjectedService")
+ private fun getRoleLaunchIntent(role: String): Intent? {
+ val packageManager = userContext.packageManager
+ val roleManager = userContext.getSystemService(RoleManager::class.java)!!
+ if (roleManager.isRoleAvailable(role)) {
+ roleManager.getDefaultApplication(role)?.let { rolePackage ->
+ packageManager.getLaunchIntentForPackage(rolePackage)?.let { return it }
+ ?: Log.w(TAG, "No launch intent for role $role")
+ } ?: Log.w(TAG, "No default application for role $role, user= ${userContext.user}")
+ } else {
+ Log.w(TAG, "Role $role is not available.")
+ }
+ return null
+ }
+
+ private fun fetchShortcutCategoryTypeByGestureType(
+ @KeyGestureType keyGestureType: Int
+ ): ShortcutCategoryType? {
+ return inputGestureMaps.gestureToShortcutCategoryTypeMap[keyGestureType]
+ }
+
+ private companion object {
+ private const val TAG = "InputGestureDataUtils"
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt
index 1c380c2..30a2f33 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt
@@ -22,14 +22,8 @@
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_HOME
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT
-import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER
-import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR
-import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR
-import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS
-import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL
-import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS
-import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN
@@ -74,13 +68,7 @@
KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT to MultiTasking,
// App Category
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR to AppCategories,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR to AppCategories,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER to AppCategories,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS to AppCategories,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL to AppCategories,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS to AppCategories,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING to AppCategories,
+ KEY_GESTURE_TYPE_LAUNCH_APPLICATION to AppCategories,
)
val gestureToInternalKeyboardShortcutGroupLabelResIdMap =
@@ -116,20 +104,14 @@
R.string.shortcutHelper_category_split_screen,
// App Category
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR to
- R.string.keyboard_shortcut_group_applications,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR to
- R.string.keyboard_shortcut_group_applications,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER to
- R.string.keyboard_shortcut_group_applications,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS to
- R.string.keyboard_shortcut_group_applications,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL to R.string.keyboard_shortcut_group_applications,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS to R.string.keyboard_shortcut_group_applications,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING to
+ KEY_GESTURE_TYPE_LAUNCH_APPLICATION to
R.string.keyboard_shortcut_group_applications,
)
+ /**
+ * App Category shortcut labels are mapped dynamically based on intent
+ * see [InputGestureDataAdapter.fetchShortcutLabelByAppLaunchData]
+ */
val gestureToInternalKeyboardShortcutInfoLabelResIdMap =
mapOf(
// System Category
@@ -158,22 +140,6 @@
R.string.system_multitasking_splitscreen_focus_lhs,
KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT to
R.string.system_multitasking_splitscreen_focus_rhs,
-
- // App Category
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR to
- R.string.keyboard_shortcut_group_applications_calculator,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR to
- R.string.keyboard_shortcut_group_applications_calendar,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER to
- R.string.keyboard_shortcut_group_applications_browser,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS to
- R.string.keyboard_shortcut_group_applications_contacts,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL to
- R.string.keyboard_shortcut_group_applications_email,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS to
- R.string.keyboard_shortcut_group_applications_maps,
- KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING to
- R.string.keyboard_shortcut_group_applications_sms,
)
val shortcutLabelToKeyGestureTypeMap: Map<String, Int>
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt
index 27d1a30..4a725ec 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt
@@ -33,6 +33,7 @@
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperExclusions
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutIcon
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
@@ -46,6 +47,7 @@
private val context: Context,
@Background private val backgroundCoroutineContext: CoroutineContext,
private val inputManager: InputManager,
+ private val shortcutHelperExclusions: ShortcutHelperExclusions,
) {
fun removeUnsupportedModifiers(modifierMask: Int): Int {
@@ -135,6 +137,8 @@
label = shortcutInfo.label,
icon = toShortcutIcon(keepIcon, shortcutInfo),
commands = listOf(shortcutCommand),
+ isCustomizable =
+ shortcutHelperExclusions.isShortcutCustomizable(shortcutInfo.label),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
index 687ad95..5060abd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
@@ -54,7 +54,7 @@
listOf(
KeyboardShortcutGroup(
resources.getString(R.string.shortcut_helper_category_system_controls),
- hardwareShortcuts(deviceId) + systemControlsShortcuts(),
+ systemControlsShortcuts() + hardwareShortcuts(deviceId),
),
KeyboardShortcutGroup(
resources.getString(R.string.shortcut_helper_category_system_apps),
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
index 2385cc6..61d11f4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
@@ -92,10 +92,8 @@
.groupBy { it.label }
.entries
.map { (commonLabel, groupedShortcuts) ->
- Shortcut(
- label = commonLabel,
- icon = groupedShortcuts.firstOrNull()?.icon,
- commands = groupedShortcuts.flatMap { it.commands },
+ groupedShortcuts[0].copy(
+ commands = groupedShortcuts.flatMap { it.commands }.sortedBy { it.keys.size },
contentDescription =
toContentDescription(commonLabel, groupedShortcuts.flatMap { it.commands }),
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt
index 9cc15ce..55cc8e0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt
@@ -21,6 +21,7 @@
val commands: List<ShortcutCommand>,
val icon: ShortcutIcon? = null,
val contentDescription: String = "",
+ val isCustomizable: Boolean = true,
) {
val containsCustomShortcutCommands: Boolean = commands.any { it.isCustom }
}
@@ -28,6 +29,7 @@
class ShortcutBuilder(private val label: String) {
val commands = mutableListOf<ShortcutCommand>()
var contentDescription = ""
+ var isCustomizable = true
fun command(builder: ShortcutCommandBuilder.() -> Unit) {
commands += ShortcutCommandBuilder().apply(builder).build()
@@ -37,7 +39,13 @@
contentDescription = string.invoke()
}
- fun build() = Shortcut(label, commands, contentDescription = contentDescription)
+ fun build() =
+ Shortcut(
+ label,
+ commands,
+ contentDescription = contentDescription,
+ isCustomizable = isCustomizable,
+ )
}
fun shortcut(label: String, block: ShortcutBuilder.() -> Unit): Shortcut =
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutHelperExclusions.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutHelperExclusions.kt
new file mode 100644
index 0000000..20b74c8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutHelperExclusions.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.keyboard.shortcut.shared.model
+
+import android.content.Context
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+class ShortcutHelperExclusions @Inject constructor(private val context: Context) {
+ private val nonCustomizableShortcutsLabels: List<String>
+ get() =
+ listOf(
+ context.getString(R.string.group_system_cycle_forward),
+ context.getString(R.string.group_system_cycle_back),
+ )
+
+ fun isShortcutCustomizable(label: String) = !nonCustomizableShortcutsLabels.contains(label)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
index 2fdcf87..2724918 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
@@ -115,6 +115,7 @@
import androidx.compose.ui.util.fastForEachIndexed
import com.android.compose.modifiers.thenIf
import com.android.compose.ui.graphics.painter.rememberDrawablePainter
+import com.android.systemui.keyboard.shortcut.shared.model.Shortcut as ShortcutModel
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo
@@ -126,7 +127,6 @@
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutsUiState
import com.android.systemui.res.R
import kotlinx.coroutines.delay
-import com.android.systemui.keyboard.shortcut.shared.model.Shortcut as ShortcutModel
@Composable
fun ShortcutHelper(
@@ -189,7 +189,7 @@
onKeyboardSettingsClicked,
shortcutsUiState.isShortcutCustomizerFlagEnabled,
onCustomizationRequested,
- shortcutsUiState.shouldShowResetButton
+ shortcutsUiState.shouldShowResetButton,
)
}
}
@@ -380,7 +380,7 @@
onKeyboardSettingsClicked: () -> Unit,
isShortcutCustomizerFlagEnabled: Boolean,
onCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit = {},
- shouldShowResetButton: Boolean
+ shouldShowResetButton: Boolean,
) {
val selectedCategory = categories.fastFirstOrNull { it.type == selectedCategoryType }
var isCustomizing by remember { mutableStateOf(false) }
@@ -565,7 +565,7 @@
modifier = Modifier.padding(vertical = 8.dp),
searchQuery = searchQuery,
shortcut = shortcut,
- isCustomizing = isCustomizing,
+ isCustomizing = isCustomizing && shortcut.isCustomizable,
onCustomizationRequested = { requestInfo ->
when (requestInfo) {
is ShortcutCustomizationRequestInfo.Add ->
@@ -801,7 +801,10 @@
private fun BoxScope.ShortcutTextKey(key: ShortcutKey.Text) {
Text(
text = key.value,
- modifier = Modifier.align(Alignment.Center).padding(horizontal = 12.dp),
+ modifier =
+ Modifier.align(Alignment.Center).padding(horizontal = 12.dp).semantics {
+ hideFromAccessibility()
+ },
style = MaterialTheme.typography.titleSmall,
)
}
@@ -825,7 +828,7 @@
Spacer(Modifier.width(spacing))
Text(
text = stringResource(R.string.shortcut_helper_key_combinations_or_separator),
- modifier = Modifier.align(Alignment.CenterVertically),
+ modifier = Modifier.align(Alignment.CenterVertically).semantics { hideFromAccessibility() },
style = MaterialTheme.typography.titleSmall,
)
Spacer(Modifier.width(spacing))
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 0101e09..096439b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -62,6 +62,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionBootInteractor;
import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule;
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransitionModule;
+import com.android.systemui.keyguard.ui.view.AlternateBouncerWindowViewBinder;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModelModule;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -248,4 +249,10 @@
@IntoMap
@ClassKey(KeyguardUpdateMonitor.class)
CoreStartable bindsKeyguardUpdateMonitor(KeyguardUpdateMonitor keyguardUpdateMonitor);
+
+ /***/
+ @Binds
+ @IntoMap
+ @ClassKey(AlternateBouncerWindowViewBinder.class)
+ CoreStartable bindsAlternateBouncerWindowViewBinder(AlternateBouncerWindowViewBinder binder);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
index d1f9fa2..e8d3bfa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
@@ -27,7 +27,6 @@
import android.service.notification.ZenModeConfig
import android.util.Log
import com.android.settingslib.notification.modes.EnableZenModeDialog
-import com.android.settingslib.notification.modes.ZenMode
import com.android.settingslib.notification.modes.ZenModeDialogMetricsLogger
import com.android.systemui.animation.Expandable
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
@@ -99,15 +98,6 @@
private var oldIsAvailable = false
private var settingsValue: Int = 0
- private val dndMode: StateFlow<ZenMode?> by lazy {
- ModesUi.assertInNewMode()
- interactor.dndMode.stateIn(
- scope = backgroundScope,
- started = SharingStarted.Eagerly,
- initialValue = null,
- )
- }
-
private val isAvailable: StateFlow<Boolean> by lazy {
ModesUi.assertInNewMode()
interactor.isZenAvailable.stateIn(
@@ -146,7 +136,7 @@
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
if (ModesUi.isEnabled) {
- combine(isAvailable, dndMode) { isAvailable, dndMode ->
+ combine(isAvailable, interactor.dndMode) { isAvailable, dndMode ->
if (!isAvailable) {
KeyguardQuickAffordanceConfig.LockScreenState.Hidden
} else if (dndMode?.isActive == true) {
@@ -222,7 +212,7 @@
if (!isAvailable.value) {
KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
} else {
- val dnd = dndMode.value
+ val dnd = interactor.dndMode.value
if (dnd == null) {
Log.wtf(TAG, "Triggered DND but it's null!?")
return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
index 71f29c0..d335a18 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
@@ -20,12 +20,12 @@
import android.content.Intent
import android.provider.Settings
import android.util.Log
-import com.android.systemui.Flags.glanceableHubShortcutButton
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.communal.data.repository.CommunalSceneRepository
import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
import com.android.systemui.dagger.SysUISingleton
@@ -46,6 +46,7 @@
@Application private val context: Context,
private val communalSceneRepository: CommunalSceneRepository,
private val communalInteractor: CommunalInteractor,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
private val sceneInteractor: SceneInteractor,
) : KeyguardQuickAffordanceConfig {
@@ -61,8 +62,7 @@
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState>
get() = flow {
emit(
- // TODO(b/378113263): Gate on getV2FlagEnabled() when ready.
- if (!glanceableHubShortcutButton()) {
+ if (!communalSettingsInteractor.isV2FlagEnabled()) {
Log.i(TAG, "Button hidden on lockscreen: flag not enabled.")
KeyguardQuickAffordanceConfig.LockScreenState.Hidden
} else if (!communalInteractor.isCommunalEnabled.value) {
@@ -81,8 +81,7 @@
}
override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
- // TODO(b/378113263): Gate on getV2FlagEnabled() when ready.
- return if (!glanceableHubShortcutButton()) {
+ return if (!communalSettingsInteractor.isV2FlagEnabled()) {
Log.i(TAG, "Button unavailable in picker: flag not enabled.")
KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
} else if (!communalInteractor.isCommunalEnabled.value) {
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 deef2a6..a999211 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
@@ -21,7 +21,6 @@
import android.app.DreamManager
import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.Flags.communalHubOnMobile
import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
@@ -49,7 +48,6 @@
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
-import kotlinx.coroutines.flow.filter
@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
@@ -178,7 +176,8 @@
newScene = CommunalScenes.Communal,
loggingReason = "FromDreamingTransitionInteractor",
transitionKey =
- if (communalHubOnMobile()) CommunalTransitionKeys.SimpleFade
+ if (communalSettingsInteractor.isV2FlagEnabled())
+ CommunalTransitionKeys.SimpleFade
else null,
)
} else {
@@ -226,8 +225,15 @@
scope.launch {
keyguardInteractor.isAbleToDream
- .filter { !it }
- .sample(deviceEntryInteractor.isUnlocked, ::Pair)
+ .filterRelevantKeyguardStateAnd { !it }
+ .sample(
+ if (SceneContainerFlag.isEnabled) {
+ deviceEntryInteractor.isUnlocked
+ } else {
+ keyguardInteractor.isKeyguardDismissible
+ },
+ ::Pair,
+ )
.collect { (_, dismissable) ->
// TODO(b/349837588): Add check for -> OCCLUDED.
if (dismissable) {
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 7cd2744..f078fe2 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
@@ -256,6 +256,16 @@
val isTransitioningBetweenDesiredScenes =
sceneInteractor.transitionState.value.isTransitioning(fromScene, toScene)
+ // When in STL A -> B settles in A we can't do the same in KTF as KTF requires us to
+ // start B -> A to get back to A. [LockscreenSceneTransitionInteractor] will emit these
+ // steps but because STL is Idle(A) at this point (and never even started a B -> A in
+ // the first place) [isTransitioningBetweenDesiredScenes] will not be satisfied. We need
+ // this condition to not filter out the STARTED and FINISHED step of the "artificially"
+ // reversed B -> A transition.
+ val belongsToInstantReversedTransition =
+ sceneInteractor.transitionState.value.isIdle(toScene) &&
+ sceneTransitionPair.value.previousValue.isTransitioning(toScene, fromScene)
+
// We can't compare the terminal step with the current sceneTransition because
// a) STL has no guarantee that it will settle in Idle() when finished/canceled
// b) Comparing to Idle(toScene) would make any other FINISHED step settling in
@@ -267,7 +277,8 @@
return@filter isTransitioningBetweenLockscreenStates ||
isTransitioningBetweenDesiredScenes ||
- terminalStepBelongsToPreviousTransition
+ terminalStepBelongsToPreviousTransition ||
+ belongsToInstantReversedTransition
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index 9df293b..3b99bb4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -276,7 +276,11 @@
false
} else if (
currentState == KeyguardState.DREAMING &&
- deviceEntryInteractor.get().isUnlocked.value
+ if (SceneContainerFlag.isEnabled) {
+ deviceEntryInteractor.get().isUnlocked.value
+ } else {
+ keyguardInteractor.isKeyguardDismissible.value
+ }
) {
// Dreams dismiss keyguard and return to GONE if they can.
false
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
index 7a36899..33783b5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -16,9 +16,7 @@
package com.android.systemui.keyguard.ui.binder
-import android.graphics.PixelFormat
import android.util.Log
-import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -36,6 +34,7 @@
import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder
import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay
import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
+import com.android.systemui.keyguard.ui.view.AlternateBouncerWindowViewLayoutParams
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
@@ -68,28 +67,6 @@
private val windowManager: Lazy<WindowManager>,
private val layoutInflater: Lazy<LayoutInflater>,
) : CoreStartable {
- private val layoutParams: WindowManager.LayoutParams
- get() =
- WindowManager.LayoutParams(
- WindowManager.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
- WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
- PixelFormat.TRANSLUCENT,
- )
- .apply {
- title = "AlternateBouncerView"
- fitInsetsTypes = 0 // overrides default, avoiding status bars during layout
- gravity = Gravity.TOP or Gravity.LEFT
- layoutInDisplayCutoutMode =
- WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
- privateFlags =
- WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY or
- WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
- // Avoid announcing window title.
- accessibilityTitle = " "
- }
private var alternateBouncerView: ConstraintLayout? = null
@@ -176,7 +153,9 @@
as ConstraintLayout
Log.d(TAG, "Adding alternate bouncer view")
- windowManager.get().addView(alternateBouncerView, layoutParams)
+ windowManager
+ .get()
+ .addView(alternateBouncerView, AlternateBouncerWindowViewLayoutParams.layoutParams)
alternateBouncerView!!.addOnAttachStateChangeListener(onAttachAddBackGestureHandler)
}
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 8cae777..273d763 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
@@ -170,6 +170,18 @@
}
}
+ disposables +=
+ keyguardRootView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.currentClock.collect { currentClock ->
+ currentClock?.apply {
+ smallClock.run { events.onThemeChanged(theme) }
+ largeClock.run { events.onThemeChanged(theme) }
+ }
+ }
+ }
+ }
+
return disposables
}
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 7d77e71..85725d2 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
@@ -368,8 +368,8 @@
SceneContainerFlag.isEnabled,
)
)
- val startPadding: Int = smartspaceViewModel.getSmartspaceStartPadding(previewContext)
- val endPadding: Int = smartspaceViewModel.getSmartspaceEndPadding(previewContext)
+ val startPadding: Int = smartspaceViewModel.getDateWeatherStartPadding(previewContext)
+ val endPadding: Int = smartspaceViewModel.getDateWeatherEndPadding(previewContext)
smartSpaceView?.let {
it.setPaddingRelative(startPadding, topPadding, endPadding, 0)
@@ -680,6 +680,7 @@
}
// In clock preview, we should have a seed color for clock
// before setting clock to clockEventController to avoid updateColor with seedColor == null
+ // So in update colors, it should already have the correct theme in clockFaceController
if (MigrateClocksToBlueprint.isEnabled) {
clockController.clock = clock
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/AlternateBouncerWindowViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/AlternateBouncerWindowViewBinder.kt
new file mode 100644
index 0000000..d3bc79e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/AlternateBouncerWindowViewBinder.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+import android.content.Context
+import android.view.View
+import android.view.WindowManager
+import android.widget.FrameLayout
+import androidx.compose.ui.platform.ComposeView
+import com.android.systemui.CoreStartable
+import com.android.systemui.compose.ComposeInitializer
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.ui.composable.AlternateBouncer
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.lifecycle.repeatWhenAttachedToWindow
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.launch
+
+/** Drives the showing and hiding of the alternate bouncer window. */
+@SysUISingleton
+class AlternateBouncerWindowViewBinder
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ @Application private val context: Context,
+ private val viewModel: AlternateBouncerViewModel,
+ private val dependencies: AlternateBouncerDependencies,
+ private val windowManager: WindowManager,
+) : CoreStartable {
+
+ override fun start() {
+ if (!SceneContainerFlag.isEnabled) {
+ return
+ }
+
+ applicationScope.launch {
+ viewModel.isVisible
+ .distinctUntilChanged()
+ .filter { it }
+ .collect {
+ windowManager.addView(
+ createView(),
+ AlternateBouncerWindowViewLayoutParams.layoutParams,
+ )
+ }
+ }
+ }
+
+ private fun createView(): View {
+ val root = FrameLayout(context)
+ val composeView =
+ ComposeView(context).apply {
+ setContent {
+ AlternateBouncer(
+ alternateBouncerDependencies = dependencies,
+ onHideAnimationFinished = {
+ if (root.isAttachedToWindow) {
+ windowManager.removeView(root)
+ }
+ },
+ )
+ }
+ }
+
+ root.repeatWhenAttached {
+ root.repeatWhenAttachedToWindow {
+ try {
+ ComposeInitializer.onAttachedToWindow(root)
+ root.addView(composeView)
+ awaitCancellation()
+ } finally {
+ root.removeView(composeView)
+ ComposeInitializer.onDetachedFromWindow(root)
+ }
+ }
+ }
+
+ return root
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/AlternateBouncerWindowViewLayoutParams.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/AlternateBouncerWindowViewLayoutParams.kt
new file mode 100644
index 0000000..5585c21
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/AlternateBouncerWindowViewLayoutParams.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.keyguard.ui.view
+
+import android.graphics.PixelFormat
+import android.view.Gravity
+import android.view.WindowManager
+
+object AlternateBouncerWindowViewLayoutParams {
+ val layoutParams: WindowManager.LayoutParams
+ get() =
+ WindowManager.LayoutParams(
+ WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
+ WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
+ PixelFormat.TRANSLUCENT,
+ )
+ .apply {
+ title = "AlternateBouncerView"
+ fitInsetsTypes = 0 // overrides default, avoiding status bars during layout
+ gravity = Gravity.TOP or Gravity.LEFT
+ layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ privateFlags =
+ WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY or
+ WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
+ // Avoid announcing window title.
+ accessibilityTitle = " "
+ }
+}
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 d54d411..73e14b1 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
@@ -33,8 +33,8 @@
import com.android.systemui.keyguard.ui.binder.KeyguardSmartspaceViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
-import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.res.R as R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shared.R as sharedR
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import dagger.Lazy
@@ -113,8 +113,9 @@
override fun applyConstraints(constraintSet: ConstraintSet) {
if (!MigrateClocksToBlueprint.isEnabled) return
if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
- val horizontalPaddingStart = KeyguardSmartspaceViewModel.getSmartspaceStartMargin(context)
- val horizontalPaddingEnd = KeyguardSmartspaceViewModel.getSmartspaceEndMargin(context)
+ val dateWeatherPaddingStart = KeyguardSmartspaceViewModel.getDateWeatherStartMargin(context)
+ val smartspaceHorizontalPadding =
+ KeyguardSmartspaceViewModel.getSmartspaceHorizontalMargin(context)
constraintSet.apply {
// migrate addDateWeatherView, addWeatherView from KeyguardClockSwitchController
constrainHeight(sharedR.id.date_smartspace_view, ConstraintSet.WRAP_CONTENT)
@@ -124,7 +125,7 @@
ConstraintSet.START,
ConstraintSet.PARENT_ID,
ConstraintSet.START,
- horizontalPaddingStart,
+ dateWeatherPaddingStart,
)
// migrate addSmartspaceView from KeyguardClockSwitchController
@@ -135,7 +136,7 @@
ConstraintSet.START,
ConstraintSet.PARENT_ID,
ConstraintSet.START,
- horizontalPaddingStart,
+ smartspaceHorizontalPadding,
)
connect(
sharedR.id.bc_smartspace_view,
@@ -143,7 +144,7 @@
if (keyguardSmartspaceViewModel.isShadeLayoutWide.value) R.id.split_shade_guideline
else ConstraintSet.PARENT_ID,
ConstraintSet.END,
- horizontalPaddingEnd,
+ smartspaceHorizontalPadding,
)
if (keyguardClockViewModel.hasCustomWeatherDataDisplay.value) {
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 0280d17..15b696e 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
@@ -62,12 +62,12 @@
overrideClockSize.value = clockSize
}
- fun getSmartspaceStartPadding(context: Context): Int {
- return KeyguardSmartspaceViewModel.getSmartspaceStartMargin(context)
+ fun getDateWeatherStartPadding(context: Context): Int {
+ return KeyguardSmartspaceViewModel.getDateWeatherStartMargin(context)
}
- fun getSmartspaceEndPadding(context: Context): Int {
- return KeyguardSmartspaceViewModel.getSmartspaceEndMargin(context)
+ fun getDateWeatherEndPadding(context: Context): Int {
+ return KeyguardSmartspaceViewModel.getDateWeatherEndMargin(context)
}
/*
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
index 3266dc4..5ee80a7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
@@ -94,14 +94,19 @@
val isShadeLayoutWide: StateFlow<Boolean> = shadeInteractor.isShadeLayoutWide
companion object {
- fun getSmartspaceStartMargin(context: Context): Int {
+ fun getDateWeatherStartMargin(context: Context): Int {
return context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_start) +
context.resources.getDimensionPixelSize(customR.dimen.status_view_margin_horizontal)
}
- fun getSmartspaceEndMargin(context: Context): Int {
+ fun getDateWeatherEndMargin(context: Context): Int {
return context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_end) +
context.resources.getDimensionPixelSize(customR.dimen.status_view_margin_horizontal)
}
+
+ fun getSmartspaceHorizontalMargin(context: Context): Int {
+ return context.resources.getDimensionPixelSize(R.dimen.smartspace_padding_horizontal) +
+ context.resources.getDimensionPixelSize(customR.dimen.status_view_margin_horizontal)
+ }
}
}
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 341b8d8..1b39d55 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
@@ -30,14 +30,10 @@
import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_DISMISS_EVENT
import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_SEEN_EVENT
import com.android.systemui.media.controls.util.SmallHash
-import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.time.SystemClock
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import java.util.Locale
import java.util.TreeMap
import javax.inject.Inject
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -49,37 +45,9 @@
constructor(
@Application private val applicationContext: Context,
private val systemClock: SystemClock,
- private val configurationController: ConfigurationController,
private val smartspaceLogger: MediaSmartspaceLogger,
) {
- val onAnyMediaConfigurationChange: Flow<Unit> = conflatedCallbackFlow {
- val callback =
- object : ConfigurationController.ConfigurationListener {
- override fun onDensityOrFontScaleChanged() {
- trySend(Unit)
- }
-
- override fun onThemeChanged() {
- trySend(Unit)
- }
-
- override fun onUiModeChanged() {
- trySend(Unit)
- }
-
- override fun onLocaleListChanged() {
- if (locale != applicationContext.resources.configuration.locales.get(0)) {
- locale = applicationContext.resources.configuration.locales.get(0)
- trySend(Unit)
- }
- }
- }
- configurationController.addCallback(callback)
- trySend(Unit)
- awaitClose { configurationController.removeCallback(callback) }
- }
-
/** Instance id of media control that recommendations card reactivated. */
private val _reactivatedId: MutableStateFlow<InstanceId?> = MutableStateFlow(null)
val reactivatedId: StateFlow<InstanceId?> = _reactivatedId.asStateFlow()
@@ -190,7 +158,7 @@
fun addMediaDataLoadingState(
mediaDataLoadingModel: MediaDataLoadingModel,
- isUpdate: Boolean = true
+ isUpdate: Boolean = true,
) {
val sortedMap = TreeMap<MediaSortKeyModel, MediaCommonModel>(comparator)
sortedMap.putAll(
@@ -395,7 +363,7 @@
logSmarspaceRecommendationCardUserEvent(
SMARTSPACE_CARD_SEEN_EVENT,
surface,
- visibleIndex
+ visibleIndex,
)
}
}
@@ -409,7 +377,7 @@
interactedSubCardRank: Int = 0,
interactedSubCardCardinality: Int = 0,
instanceId: InstanceId? = null,
- isRec: Boolean = false
+ isRec: Boolean = false,
) {
_currentMedia.value.forEachIndexed { index, mediaCommonModel ->
when (mediaCommonModel) {
@@ -423,7 +391,7 @@
surface,
mediaCommonModel.mediaLoadedModel.isSsReactivated,
interactedSubCardRank,
- interactedSubCardCardinality
+ interactedSubCardCardinality,
)
}
return
@@ -437,7 +405,7 @@
surface,
index,
interactedSubCardRank,
- interactedSubCardCardinality
+ interactedSubCardCardinality,
)
}
return
@@ -459,14 +427,14 @@
SMARTSPACE_CARD_DISMISS_EVENT,
surface,
mediaCommonModel.mediaLoadedModel.isSsReactivated,
- isSwipeToDismiss = true
+ isSwipeToDismiss = true,
)
is MediaCommonModel.MediaRecommendations ->
logSmarspaceRecommendationCardUserEvent(
SMARTSPACE_CARD_DISMISS_EVENT,
surface,
index,
- isSwipeToDismiss = true
+ isSwipeToDismiss = true,
)
}
}
@@ -513,7 +481,7 @@
isReactivated: Boolean,
interactedSubCardRank: Int = 0,
interactedSubCardCardinality: Int = 0,
- isSwipeToDismiss: Boolean = false
+ isSwipeToDismiss: Boolean = false,
) {
_selectedUserEntries.value[instanceId]?.let {
smartspaceLogger.logSmartspaceCardUIEvent(
@@ -537,7 +505,7 @@
index: Int,
interactedSubCardRank: Int = 0,
interactedSubCardCardinality: Int = 0,
- isSwipeToDismiss: Boolean = false
+ isSwipeToDismiss: Boolean = false,
) {
smartspaceLogger.logSmartspaceCardUIEvent(
eventId,
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 1f339dd..09aa85b 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
@@ -68,8 +68,6 @@
.map { entries -> entries[instanceId]?.let { toMediaControlModel(it) } }
.distinctUntilChanged()
- val onAnyMediaConfigurationChange: Flow<Unit> = repository.onAnyMediaConfigurationChange
-
fun removeMediaControl(
token: MediaSession.Token?,
instanceId: InstanceId,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt
index c3a36b2..48ed391 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt
@@ -66,14 +66,12 @@
.distinctUntilChanged()
.stateIn(applicationScope, SharingStarted.WhileSubscribed(), false)
- val onAnyMediaConfigurationChange: Flow<Unit> = repository.onAnyMediaConfigurationChange
-
fun removeMediaRecommendations(
key: String,
dismissIntent: Intent?,
delayMs: Long,
eventId: Int,
- location: Int
+ location: Int,
) {
logSmartspaceCardUserEvent(eventId, location)
mediaDataProcessor.dismissSmartspaceRecommendation(key, delayMs)
@@ -101,7 +99,7 @@
eventId: Int,
location: Int,
interactedSubCardRank: Int,
- interactedSubCardCardinality: Int
+ interactedSubCardCardinality: Int,
) {
if (interactedSubCardRank == -1) {
logSmartspaceCardUserEvent(eventId, MediaSmartspaceLogger.getSurface(location))
@@ -111,7 +109,7 @@
MediaSmartspaceLogger.getSurface(location),
interactedSubCardRank = interactedSubCardRank,
interactedSubCardCardinality = interactedSubCardCardinality,
- isRec = true
+ isRec = true,
)
}
if (shouldActivityOpenInForeground(intent)) {
@@ -121,7 +119,7 @@
0 /* delay */,
expandable.activityTransitionController(
InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER
- )
+ ),
)
} else {
// Otherwise, open the activity in background directly.
@@ -133,7 +131,7 @@
repository.logSmartspaceCardUserEvent(
eventId,
MediaSmartspaceLogger.getSurface(location),
- isRec = true
+ isRec = true,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
index a6e1582..910d3a8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
@@ -34,6 +34,7 @@
import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.settingslib.widget.AdaptiveIcon
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
@@ -64,7 +65,6 @@
import com.android.systemui.surfaceeffects.ripple.RippleShader
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.collectLatest
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
private const val TAG = "MediaControlViewBinder"
@@ -85,7 +85,7 @@
launch {
viewModel.player.collectLatest { player ->
player?.let {
- if (viewModel.isNewPlayer(it)) {
+ if (viewModel.setPlayer(it)) {
bindMediaCard(
viewHolder,
viewController,
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 43c2011..f0f8a95 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
@@ -37,6 +37,7 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.DiffUtil
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.app.tracing.traceSection
import com.android.internal.logging.InstanceId
import com.android.keyguard.KeyguardUpdateMonitor
@@ -115,7 +116,6 @@
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
private const val TAG = "MediaCarouselController"
@@ -752,7 +752,11 @@
}
}
- private fun onAdded(commonViewModel: MediaCommonViewModel, position: Int) {
+ private fun onAdded(
+ commonViewModel: MediaCommonViewModel,
+ position: Int,
+ configChanged: Boolean = false,
+ ) {
val viewController = mediaViewControllerFactory.get()
viewController.sizeChangedListener = this::updateCarouselDimensions
val lp =
@@ -763,12 +767,13 @@
when (commonViewModel) {
is MediaCommonViewModel.MediaControl -> {
val viewHolder = MediaViewHolder.create(LayoutInflater.from(context), mediaContent)
- if (SceneContainerFlag.isEnabled) {
- viewController.widthInSceneContainerPx = widthInSceneContainerPx
- viewController.heightInSceneContainerPx = heightInSceneContainerPx
- }
+ viewController.widthInSceneContainerPx = widthInSceneContainerPx
+ viewController.heightInSceneContainerPx = heightInSceneContainerPx
viewController.attachPlayer(viewHolder)
viewController.mediaViewHolder?.player?.layoutParams = lp
+ if (configChanged) {
+ commonViewModel.controlViewModel.onMediaConfigChanged()
+ }
MediaControlViewBinder.bind(
viewHolder,
commonViewModel.controlViewModel,
@@ -1271,23 +1276,14 @@
ColorStateList.valueOf(context.getColor(R.color.media_paging_indicator))
if (recreateMedia) {
mediaContent.removeAllViews()
- commonViewModels.forEach { viewModel ->
+ commonViewModels.forEachIndexed { index, viewModel ->
when (viewModel) {
- is MediaCommonViewModel.MediaControl -> {
- controllerById[viewModel.instanceId.toString()]?.let {
- it.widthInSceneContainerPx = widthInSceneContainerPx
- it.heightInSceneContainerPx = heightInSceneContainerPx
- mediaContent.addView(it.mediaViewHolder?.player)
- }
- }
- is MediaCommonViewModel.MediaRecommendations -> {
- controllerById[viewModel.key]?.let {
- it.widthInSceneContainerPx = widthInSceneContainerPx
- it.heightInSceneContainerPx = heightInSceneContainerPx
- mediaContent.addView(it.recommendationViewHolder?.recommendations)
- }
- }
+ is MediaCommonViewModel.MediaControl ->
+ controllerById[viewModel.instanceId.toString()]?.onDestroy()
+ is MediaCommonViewModel.MediaRecommendations ->
+ controllerById[viewModel.key]?.onDestroy()
}
+ onAdded(viewModel, index, configChanged = true)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
index 4173d2a..4e97f20 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
@@ -41,10 +41,8 @@
import com.android.systemui.res.R
import java.util.concurrent.Executor
import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
@@ -56,15 +54,9 @@
private val interactor: MediaControlInteractor,
private val logger: MediaUiEventLogger,
) {
-
- @OptIn(ExperimentalCoroutinesApi::class)
val player: Flow<MediaPlayerViewModel?> =
- interactor.onAnyMediaConfigurationChange
- .flatMapLatest {
- interactor.mediaControl.map { mediaControl ->
- mediaControl?.let { toViewModel(it) }
- }
- }
+ interactor.mediaControl
+ .map { mediaControl -> mediaControl?.let { toViewModel(it) } }
.distinctUntilChanged { old, new ->
(new == null && old == null) || new?.contentEquals(old) ?: false
}
@@ -74,14 +66,21 @@
private var isAnyButtonClicked = false
@MediaLocation private var location = MediaHierarchyManager.LOCATION_UNKNOWN
private var playerViewModel: MediaPlayerViewModel? = null
+ private var allowPlayerUpdate: Boolean = false
- fun isNewPlayer(viewModel: MediaPlayerViewModel): Boolean {
- val contentEquals = playerViewModel?.contentEquals(viewModel) ?: false
- return (!contentEquals).also { playerViewModel = viewModel }
+ fun setPlayer(viewModel: MediaPlayerViewModel): Boolean {
+ val tempViewModel = playerViewModel
+ playerViewModel = viewModel
+ return allowPlayerUpdate || !(tempViewModel?.contentEquals(viewModel) ?: false)
+ }
+
+ fun onMediaConfigChanged() {
+ allowPlayerUpdate = true
}
fun onMediaControlIsBound(artistName: CharSequence, titleName: CharSequence) {
interactor.logMediaControlIsBound(artistName, titleName)
+ allowPlayerUpdate = false
}
private fun onDismissMediaData(
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 6bc6b10a..88cfbaf 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
@@ -41,10 +41,8 @@
import com.android.systemui.res.R
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
@@ -59,12 +57,9 @@
private val logger: MediaUiEventLogger,
) {
- @OptIn(ExperimentalCoroutinesApi::class)
val mediaRecsCard: Flow<MediaRecsCardViewModel?> =
- interactor.onAnyMediaConfigurationChange
- .flatMapLatest {
- interactor.recommendations.map { recsCard -> toRecsViewModel(recsCard) }
- }
+ interactor.recommendations
+ .map { recsCard -> toRecsViewModel(recsCard) }
.distinctUntilChanged()
.flowOn(backgroundDispatcher)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/OWNERS b/packages/SystemUI/src/com/android/systemui/media/dialog/OWNERS
index 95b8fa7..4976d94 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/OWNERS
@@ -1,3 +1,3 @@
# Bug component: 1280508
-# Files in this directory should still be reviewed by a member of SystemUI team
+asapperstein@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
index 32de56f..e17255a 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
@@ -50,22 +50,29 @@
@ScreenShareMode val defaultSelectedMode: Int = screenShareOptions.first().mode,
) : DialogDelegate<T>, AdapterView.OnItemSelectedListener {
private lateinit var dialogTitle: TextView
- private lateinit var startButton: TextView
private lateinit var cancelButton: TextView
- private lateinit var warning: TextView
private lateinit var screenShareModeSpinner: Spinner
protected lateinit var dialog: AlertDialog
- private var shouldLogCancel: Boolean = true
- var selectedScreenShareOption: ScreenShareOption =
- screenShareOptions.first { it.mode == defaultSelectedMode }
+ protected lateinit var viewBinder: BaseMediaProjectionPermissionViewBinder
+
+ /**
+ * Create the view binder for the permission dialog, this can be override by child classes to
+ * support a different type of view binder
+ */
+ open fun createViewBinder(): BaseMediaProjectionPermissionViewBinder {
+ return BaseMediaProjectionPermissionViewBinder(
+ screenShareOptions,
+ appName,
+ hostUid,
+ mediaProjectionMetricsLogger,
+ defaultSelectedMode,
+ dialog,
+ )
+ }
@CallSuper
override fun onStop(dialog: T) {
- // onStop can be called multiple times and we only want to log once.
- if (shouldLogCancel) {
- mediaProjectionMetricsLogger.notifyProjectionRequestCancelled(hostUid)
- shouldLogCancel = false
- }
+ viewBinder.unbind()
}
@CallSuper
@@ -75,12 +82,14 @@
dialog.window?.setGravity(Gravity.CENTER)
dialog.setContentView(R.layout.screen_share_dialog)
dialogTitle = dialog.requireViewById(R.id.screen_share_dialog_title)
- warning = dialog.requireViewById(R.id.text_warning)
- startButton = dialog.requireViewById(android.R.id.button1)
cancelButton = dialog.requireViewById(android.R.id.button2)
updateIcon()
- initScreenShareOptions()
createOptionsView(getOptionsViewLayoutId())
+ if (!::viewBinder.isInitialized) {
+ viewBinder = createViewBinder()
+ }
+ viewBinder.bind()
+ initScreenShareSpinner()
}
private fun updateIcon() {
@@ -93,18 +102,6 @@
}
}
- private fun initScreenShareOptions() {
- selectedScreenShareOption = screenShareOptions.first { it.mode == defaultSelectedMode }
- setOptionSpecificFields()
- initScreenShareSpinner()
- }
-
- private val warningText: String
- get() = dialog.context.getString(selectedScreenShareOption.warningText, appName)
-
- private val startButtonText: String
- get() = dialog.context.getString(selectedScreenShareOption.startButtonText)
-
private fun initScreenShareSpinner() {
val adapter = OptionsAdapter(dialog.context.applicationContext, screenShareOptions)
screenShareModeSpinner = dialog.requireViewById(R.id.screen_share_mode_options)
@@ -128,18 +125,15 @@
}
override fun onItemSelected(adapterView: AdapterView<*>?, view: View, pos: Int, id: Long) {
- selectedScreenShareOption = screenShareOptions[pos]
- setOptionSpecificFields()
- }
-
- /** Sets fields on the dialog that change based on which option is selected. */
- private fun setOptionSpecificFields() {
- warning.text = warningText
- startButton.text = startButtonText
+ viewBinder.onItemSelected(pos)
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
+ fun getSelectedScreenShareOption(): ScreenShareOption {
+ return viewBinder.selectedScreenShareOption
+ }
+
/** Protected methods for the text updates & functionality */
protected fun setDialogTitle(@StringRes stringId: Int) {
val title = dialog.context.getString(stringId, appName)
@@ -147,10 +141,7 @@
}
protected fun setStartButtonOnClickListener(listener: View.OnClickListener?) {
- startButton.setOnClickListener { view ->
- shouldLogCancel = false
- listener?.onClick(view)
- }
+ viewBinder.setStartButtonOnClickListener(listener)
}
protected fun setCancelButtonOnClickListener(listener: View.OnClickListener?) {
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
new file mode 100644
index 0000000..728255d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.mediaprojection.permission
+
+import android.app.AlertDialog
+import android.view.View
+import android.widget.TextView
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
+import com.android.systemui.res.R
+
+open class BaseMediaProjectionPermissionViewBinder(
+ private val screenShareOptions: List<ScreenShareOption>,
+ private val appName: String?,
+ private val hostUid: Int,
+ private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
+ @ScreenShareMode val defaultSelectedMode: Int = screenShareOptions.first().mode,
+ private val dialog: AlertDialog,
+) {
+ private lateinit var warning: TextView
+ private lateinit var startButton: TextView
+ var selectedScreenShareOption: ScreenShareOption =
+ screenShareOptions.first { it.mode == defaultSelectedMode }
+ private var shouldLogCancel: Boolean = true
+
+ fun unbind() {
+ // unbind can be called multiple times and we only want to log once.
+ if (shouldLogCancel) {
+ mediaProjectionMetricsLogger.notifyProjectionRequestCancelled(hostUid)
+ shouldLogCancel = false
+ }
+ }
+
+ open fun bind() {
+ warning = dialog.requireViewById(R.id.text_warning)
+ startButton = dialog.requireViewById(android.R.id.button1)
+ initScreenShareOptions()
+ }
+
+ private fun initScreenShareOptions() {
+ selectedScreenShareOption = screenShareOptions.first { it.mode == defaultSelectedMode }
+ setOptionSpecificFields()
+ }
+
+ /** Sets fields on the dialog that change based on which option is selected. */
+ private fun setOptionSpecificFields() {
+ warning.text = warningText
+ startButton.text = startButtonText
+ }
+
+ open fun onItemSelected(pos: Int) {
+ selectedScreenShareOption = screenShareOptions[pos]
+ setOptionSpecificFields()
+ }
+
+ private val warningText: String
+ get() = dialog.context.getString(selectedScreenShareOption.warningText, appName)
+
+ private val startButtonText: String
+ get() = dialog.context.getString(selectedScreenShareOption.startButtonText)
+
+ fun setStartButtonOnClickListener(listener: View.OnClickListener?) {
+ startButton.setOnClickListener { view ->
+ shouldLogCancel = false
+ listener?.onClick(view)
+ }
+ }
+}
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 2fda201..d33ad8f 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -122,21 +122,20 @@
final Intent launchingIntent = getIntent();
mReviewGrantedConsentRequired = launchingIntent.getBooleanExtra(
EXTRA_USER_REVIEW_GRANTED_CONSENT, false);
- if (com.android.systemui.Flags.mediaProjectionRequestAttributionFix()) {
- mPackageName = getLaunchedFromPackage();
- } else {
- mPackageName = getCallingPackage();
- }
- // This activity is launched directly by an app, or system server. System server provides
- // the package name through the intent if so.
- if (mPackageName == null || (
- com.android.systemui.Flags.mediaProjectionRequestAttributionFix()
- && getCallingPackage() == null)) {
+ // The original requester of this activity start
+ mPackageName = getLaunchedFromPackage();
+
+ // This activity is launched directly by using startActivity(),
+ // thus getCallingPackage() will be null.
+ if (getCallingPackage() == null) {
+ // System server provides the package name through the intent if so and is able to get
+ // the result back. Other applications can't.
if (launchingIntent.hasExtra(EXTRA_PACKAGE_REUSING_GRANTED_CONSENT)) {
mPackageName = launchingIntent.getStringExtra(
EXTRA_PACKAGE_REUSING_GRANTED_CONSENT);
} else {
+ // The activity was not launched for result, we abort here
finishAsCancelled();
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index ba3357c..7c7f48e0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -19,6 +19,8 @@
import android.content.res.Resources;
import android.provider.Settings;
+import androidx.annotation.NonNull;
+
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.res.R;
@@ -80,6 +82,12 @@
void addTile(ComponentName tile);
/**
+ * Click on a tile. Used by external commands
+ * @param tile the component name of the {@link android.service.quicksettings.TileService}
+ */
+ void clickTile(@NonNull ComponentName tile);
+
+ /**
* Adds a custom tile to the set of current tiles.
* @param tile the component name of the {@link android.service.quicksettings.TileService}
* @param end if true, the tile will be added at the end. If false, at the beginning.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
index 0d464f5..dc3b582 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
@@ -19,11 +19,13 @@
import android.content.ComponentName
import android.content.Context
import androidx.annotation.GuardedBy
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.external.TileServiceRequestController
+import com.android.systemui.qs.flags.QsInCompose
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository.Companion.POSITION_AT_END
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
@@ -32,7 +34,6 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Adapter to determine what real class to use for classes that depend on [QSHost].
@@ -135,4 +136,12 @@
override fun indexOf(tileSpec: String): Int {
return specs.indexOf(tileSpec)
}
+
+ override fun clickTile(tile: ComponentName) {
+ if (QsInCompose.isUnexpectedlyInLegacyMode()) {
+ return
+ }
+ val spec = TileSpec.create(tile)
+ interactor.currentTiles.value.firstOrNull { it.spec == spec }?.tile?.click(null)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index 9dc21fb..8d9f49e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -47,7 +47,6 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.verticalScroll
@@ -248,7 +247,7 @@
PlatformTheme(isDarkTheme = true) {
ProvideShortcutHelperIndication(interactionsConfig = interactionsConfig()) {
AnimatedVisibility(
- visible = viewModel.isQsVisible,
+ visible = viewModel.isQsVisibleAndAnyShadeExpanded,
modifier =
Modifier.graphicsLayer { alpha = viewModel.viewAlpha }
// Clipping before translation to match QSContainerImpl.onDraw
@@ -709,12 +708,7 @@
GridAnchor()
TileGrid(
viewModel = containerViewModel.tileGridViewModel,
- modifier =
- Modifier.fillMaxWidth()
- .heightIn(
- max =
- QuickSettingsShade.Dimensions.GridMaxHeight
- ),
+ modifier = Modifier.fillMaxWidth(),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
index 02498d6..3c72520 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
@@ -62,6 +62,7 @@
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -101,6 +102,7 @@
DisableFlagsInteractor: DisableFlagsInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val largeScreenShadeInterpolator: LargeScreenShadeInterpolator,
+ private val shadeInteractor: ShadeInteractor,
@ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
private val squishinessInteractor: TileSquishinessInteractor,
@@ -129,6 +131,9 @@
var isQsVisible by mutableStateOf(false)
+ val isQsVisibleAndAnyShadeExpanded: Boolean
+ get() = anyShadeExpanded && isQsVisible
+
// This can only be negative if undefined (in which case it will be -1f), else it will be
// in [0, 1]. In some cases, it could be set back to -1f internally to indicate that it's
// different to every value in [0, 1].
@@ -429,6 +434,12 @@
),
)
+ private val anyShadeExpanded by
+ hydrator.hydratedStateOf(
+ traceName = "anyShadeExpanded",
+ source = shadeInteractor.isAnyExpanded,
+ )
+
fun applyNewQsScrollerBounds(left: Float, top: Float, right: Float, bottom: Float) {
if (usingMedia) {
qsMediaHost.currentClipping.set(
@@ -503,6 +514,8 @@
printSection("Quick Settings state") {
println("isQSExpanded", isQsExpanded)
println("isQSVisible", isQsVisible)
+ println("anyShadeExpanded", anyShadeExpanded)
+ println("isQSVisibleAndAnyShadeExpanded", isQsVisibleAndAnyShadeExpanded)
println("isQSEnabled", isQsEnabled)
println("isCustomizing", containerViewModel.editModeViewModel.isEditing.value)
println("inFirstPage", inFirstPage)
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 94b8a3a..1205c87 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -16,55 +16,10 @@
package com.android.systemui.qs.dagger;
-import com.android.systemui.media.dagger.MediaModule;
-import com.android.systemui.qs.ReduceBrightColorsController;
-import com.android.systemui.qs.ReduceBrightColorsControllerImpl;
-import com.android.systemui.qs.composefragment.dagger.QSFragmentComposeModule;
-import com.android.systemui.qs.external.QSExternalModule;
-import com.android.systemui.qs.panels.dagger.PanelsModule;
-import com.android.systemui.qs.pipeline.dagger.QSPipelineModule;
-import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.qs.tiles.di.QSTilesModule;
-import com.android.systemui.qs.ui.adapter.QSSceneAdapter;
-import com.android.systemui.qs.ui.adapter.QSSceneAdapterImpl;
-
-import java.util.Map;
-
-import dagger.Binds;
import dagger.Module;
-import dagger.multibindings.Multibinds;
/**
- * Module for QS dependencies
+ * Module for QS dependencies for AOSP inclusion
*/
-@Module(subcomponents = {QSFragmentComponent.class, QSSceneComponent.class},
- includes = {
- MediaModule.class,
- PanelsModule.class,
- QSFragmentComposeModule.class,
- QSExternalModule.class,
- QSFlagsModule.class,
- QSHostModule.class,
- QSPipelineModule.class,
- QSTilesModule.class,
- }
-)
-public interface QSModule {
-
- /**
- * A map of internal QS tiles. Ensures that this can be injected even if
- * it is empty
- */
- @Multibinds
- Map<String, QSTileImpl<?>> tileMap();
-
- @Binds
- QSSceneAdapter bindsQsSceneInteractor(QSSceneAdapterImpl impl);
-
- /**
- * Dims the screen
- */
- @Binds
- ReduceBrightColorsController bindReduceBrightColorsController(
- ReduceBrightColorsControllerImpl impl);
-}
+@Module(includes = { QSModuleBase.class})
+public interface QSModule { }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModuleBase.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModuleBase.kt
new file mode 100644
index 0000000..3fd8768
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModuleBase.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dagger
+
+import com.android.systemui.media.dagger.MediaModule
+import com.android.systemui.qs.ReduceBrightColorsController
+import com.android.systemui.qs.ReduceBrightColorsControllerImpl
+import com.android.systemui.qs.composefragment.dagger.QSFragmentComposeModule
+import com.android.systemui.qs.external.QSExternalModule
+import com.android.systemui.qs.panels.dagger.PanelsModule
+import com.android.systemui.qs.pipeline.dagger.QSPipelineModule
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.di.QSTilesModule
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
+import com.android.systemui.qs.ui.adapter.QSSceneAdapterImpl
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.Multibinds
+
+/**
+ * QS Module for shared dependencies between AOSP and variants. Include this module in more
+ * specialized modules (like [QSModule]) and do not include this module directly in SystemUI modules
+ */
+@Module(
+ subcomponents = [QSFragmentComponent::class, QSSceneComponent::class],
+ includes =
+ [
+ MediaModule::class,
+ PanelsModule::class,
+ QSFragmentComposeModule::class,
+ QSExternalModule::class,
+ QSFlagsModule::class,
+ QSHostModule::class,
+ QSPipelineModule::class,
+ QSTilesModule::class,
+ ],
+)
+interface QSModuleBase {
+
+ /** A map of internal QS tiles. Ensures that this can be injected even if it is empty */
+ @Multibinds fun tileMap(): Map<String?, QSTileImpl<*>?>?
+
+ @Binds fun bindsQsSceneAdapter(impl: QSSceneAdapterImpl?): QSSceneAdapter?
+
+ /** Dims the screen */
+ @Binds
+ fun bindReduceBrightColorsController(
+ impl: ReduceBrightColorsControllerImpl?
+ ): ReduceBrightColorsController?
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index 8ef6375..cc87206 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -23,12 +23,12 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.LifecycleOwner
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.settingslib.Utils
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.globalactions.GlobalActionsDialogLite
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
@@ -38,6 +38,7 @@
import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.shade.shared.flag.DualShade
import com.android.systemui.util.icuMessageFormat
import javax.inject.Inject
import javax.inject.Named
@@ -54,7 +55,6 @@
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.isActive
-import com.android.app.tracing.coroutines.launchTraced as launch
private const val TAG = "FooterActionsViewModel"
@@ -113,7 +113,7 @@
class Factory
@Inject
constructor(
- @ShadeDisplayAware private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val falsingManager: FalsingManager,
private val footerActionsInteractor: FooterActionsInteractor,
private val globalActionsDialogLiteProvider: Provider<GlobalActionsDialogLite>,
@@ -211,7 +211,7 @@
false /* if the dismiss should be deferred */
},
null /* cancelAction */,
- true /* afterKeyguardGone */
+ true, /* afterKeyguardGone */
)
}
@@ -269,29 +269,7 @@
.distinctUntilChanged()
val userSwitcher =
- footerActionsInteractor.userSwitcherStatus
- .map { userSwitcherStatus ->
- when (userSwitcherStatus) {
- UserSwitcherStatusModel.Disabled -> null
- is UserSwitcherStatusModel.Enabled -> {
- if (userSwitcherStatus.currentUserImage == null) {
- Log.e(
- TAG,
- "Skipped the addition of user switcher button because " +
- "currentUserImage is missing",
- )
- return@map null
- }
-
- userSwitcherButtonViewModel(
- qsThemedContext,
- userSwitcherStatus,
- ::onUserSwitcherClicked
- )
- }
- }
- }
- .distinctUntilChanged()
+ userSwitcherViewModel(qsThemedContext, footerActionsInteractor, ::onUserSwitcherClicked)
val settings = settingsButtonViewModel(qsThemedContext, ::onSettingsButtonClicked)
val power =
@@ -311,6 +289,36 @@
)
}
+fun userSwitcherViewModel(
+ themedContext: Context,
+ footerActionsInteractor: FooterActionsInteractor,
+ onUserSwitcherClicked: (Expandable) -> Unit,
+): Flow<FooterActionsButtonViewModel?> {
+ return footerActionsInteractor.userSwitcherStatus
+ .map { userSwitcherStatus ->
+ when (userSwitcherStatus) {
+ UserSwitcherStatusModel.Disabled -> null
+ is UserSwitcherStatusModel.Enabled -> {
+ if (userSwitcherStatus.currentUserImage == null) {
+ Log.e(
+ TAG,
+ "Skipped the addition of user switcher button because " +
+ "currentUserImage is missing",
+ )
+ return@map null
+ }
+
+ userSwitcherButtonViewModel(
+ themedContext,
+ userSwitcherStatus,
+ onUserSwitcherClicked,
+ )
+ }
+ }
+ }
+ .distinctUntilChanged()
+}
+
fun securityButtonViewModel(
config: SecurityButtonConfig,
onSecurityButtonClicked: (Context, Expandable) -> Unit,
@@ -369,7 +377,7 @@
private fun userSwitcherContentDescription(
qsThemedContext: Context,
- currentUser: String?
+ currentUser: String?,
): String? {
return currentUser?.let { user ->
qsThemedContext.getString(R.string.accessibility_quick_settings_user, user)
@@ -384,13 +392,9 @@
id = R.id.settings_button_container,
Icon.Resource(
R.drawable.ic_settings,
- ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
+ ContentDescription.Resource(R.string.accessibility_quick_settings_settings),
),
- iconTint =
- Utils.getColorAttrDefaultColor(
- qsThemedContext,
- R.attr.onShadeInactiveVariant,
- ),
+ iconTint = Utils.getColorAttrDefaultColor(qsThemedContext, R.attr.onShadeInactiveVariant),
backgroundColor = R.attr.shadeInactive,
onSettingsButtonClicked,
)
@@ -404,14 +408,14 @@
id = R.id.pm_lite,
Icon.Resource(
android.R.drawable.ic_lock_power_off,
- ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
+ ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu),
),
iconTint =
Utils.getColorAttrDefaultColor(
qsThemedContext,
- R.attr.onShadeActive,
+ if (DualShade.isEnabled) R.attr.onShadeInactiveVariant else R.attr.onShadeActive,
),
- backgroundColor = R.attr.shadeActive,
+ backgroundColor = if (DualShade.isEnabled) R.attr.shadeInactive else R.attr.shadeActive,
onPowerButtonClicked,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
index ead38f3..ef45ae76 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -209,7 +209,7 @@
// TODO(b/250618218): Remove this method once we know the root cause of b/250618218.
fun logTileBackgroundColorUpdateIfInternetTile(
- tileSpec: String,
+ tileSpec: String?,
state: Int,
disabledByPolicy: Boolean,
color: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
index 1f55ac7..f4bf53ca 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
@@ -21,8 +21,6 @@
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepository
import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepositoryImpl
-import com.android.systemui.qs.panels.data.repository.GridLayoutTypeRepository
-import com.android.systemui.qs.panels.data.repository.GridLayoutTypeRepositoryImpl
import com.android.systemui.qs.panels.domain.interactor.EditTilesResetInteractor
import com.android.systemui.qs.panels.domain.interactor.SizedTilesResetInteractor
import com.android.systemui.qs.panels.shared.model.GridLayoutType
@@ -49,9 +47,6 @@
): DefaultLargeTilesRepository
@Binds
- fun bindGridLayoutTypeRepository(impl: GridLayoutTypeRepositoryImpl): GridLayoutTypeRepository
-
- @Binds
fun bindEditTilesResetInteractor(impl: SizedTilesResetInteractor): EditTilesResetInteractor
@Binds fun bindIconTilesViewModel(impl: IconTilesViewModelImpl): IconTilesViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt
index 47c4ffd..f17abe8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt
@@ -17,28 +17,14 @@
package com.android.systemui.qs.panels.data.repository
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.panels.shared.model.GridLayoutType
+import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
import com.android.systemui.qs.panels.shared.model.PaginatedGridLayoutType
import javax.inject.Inject
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-
-interface GridLayoutTypeRepository {
- val layout: StateFlow<GridLayoutType>
-
- fun setLayout(type: GridLayoutType)
-}
+import kotlinx.coroutines.flow.flowOf
@SysUISingleton
-class GridLayoutTypeRepositoryImpl @Inject constructor() : GridLayoutTypeRepository {
- private val _layout: MutableStateFlow<GridLayoutType> =
- MutableStateFlow(PaginatedGridLayoutType)
- override val layout = _layout.asStateFlow()
+class GridLayoutTypeRepository @Inject constructor() {
+ val defaultLayoutType = flowOf(PaginatedGridLayoutType)
- override fun setLayout(type: GridLayoutType) {
- if (_layout.value != type) {
- _layout.value = type
- }
- }
+ val dualShadeLayoutType = flowOf(InfiniteGridLayoutType)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt
index 4af1b22..e493cbe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt
@@ -19,14 +19,23 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.panels.data.repository.GridLayoutTypeRepository
import com.android.systemui.qs.panels.shared.model.GridLayoutType
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
@SysUISingleton
-class GridLayoutTypeInteractor @Inject constructor(private val repo: GridLayoutTypeRepository) {
- val layout: StateFlow<GridLayoutType> = repo.layout
-
- fun setLayoutType(type: GridLayoutType) {
- repo.setLayout(type)
- }
+@OptIn(ExperimentalCoroutinesApi::class)
+class GridLayoutTypeInteractor
+@Inject
+constructor(private val repo: GridLayoutTypeRepository, shadeModeInteractor: ShadeModeInteractor) {
+ val layout: Flow<GridLayoutType> =
+ shadeModeInteractor.shadeMode.flatMapLatest { shadeMode ->
+ when (shadeMode) {
+ is ShadeMode.Dual -> repo.dualShadeLayoutType
+ else -> repo.defaultLayoutType
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
index b6dbf4d..39408d3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
@@ -49,9 +49,10 @@
import com.android.systemui.qs.panels.dagger.PaginatedBaseLayoutType
import com.android.systemui.qs.panels.ui.compose.Dimensions.FooterHeight
import com.android.systemui.qs.panels.ui.compose.Dimensions.InterPageSpacing
-import com.android.systemui.qs.panels.ui.viewmodel.EditModeButtonViewModel
+import com.android.systemui.qs.panels.ui.compose.toolbar.EditModeButton
import com.android.systemui.qs.panels.ui.viewmodel.PaginatedGridViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.toolbar.EditModeButtonViewModel
import com.android.systemui.qs.ui.compose.borderOnFocus
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
index c6141a1..a05747d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
@@ -28,6 +28,7 @@
import androidx.compose.foundation.LocalOverscrollFactory
import androidx.compose.foundation.background
import androidx.compose.foundation.border
+import androidx.compose.foundation.clipScrollableContainer
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Box
@@ -138,7 +139,6 @@
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.model.groupAndSort
import com.android.systemui.res.R
-import kotlin.math.max
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
@@ -148,8 +148,9 @@
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun EditModeTopBar(onStopEditing: () -> Unit, onReset: (() -> Unit)?) {
+
TopAppBar(
- colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Black),
+ colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent),
title = { Text(text = stringResource(id = R.string.qs_edit)) },
navigationIcon = {
IconButton(onClick = onStopEditing) {
@@ -209,7 +210,15 @@
Column(
verticalArrangement =
spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
- modifier = modifier.fillMaxSize().verticalScroll(scrollState).padding(innerPadding),
+ modifier =
+ modifier
+ .fillMaxSize()
+ // Apply top padding before the scroll so the scrollable doesn't show under
+ // the
+ // top bar
+ .padding(top = innerPadding.calculateTopPadding())
+ .clipScrollableContainer(Orientation.Vertical)
+ .verticalScroll(scrollState),
) {
AnimatedContent(
targetState = listState.dragInProgress,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
index c798e5b..13b3311 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
@@ -126,7 +126,7 @@
modifier: Modifier = Modifier,
detailsViewModel: DetailsViewModel?,
) {
- val state by tile.state.collectAsStateWithLifecycle(tile.currentState)
+ val state: QSTile.State by tile.state.collectAsStateWithLifecycle(tile.currentState)
val currentBounceableInfo by rememberUpdatedState(bounceableInfo)
val resources = resources()
val uiState = remember(state, resources) { state.toUiState(resources) }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditModeButton.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/EditModeButton.kt
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditModeButton.kt
rename to packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/EditModeButton.kt
index c2764f9..85db952 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditModeButton.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/EditModeButton.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.ui.compose
+package com.android.systemui.qs.panels.ui.compose.toolbar
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -30,7 +30,7 @@
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.android.systemui.lifecycle.rememberViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.EditModeButtonViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.toolbar.EditModeButtonViewModel
import com.android.systemui.qs.ui.compose.borderOnFocus
import com.android.systemui.res.R
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/Toolbar.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/Toolbar.kt
new file mode 100644
index 0000000..37fa9e7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/Toolbar.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.qs.panels.ui.compose.toolbar
+
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.requiredHeight
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.android.systemui.compose.modifiers.sysuiResTag
+import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.qs.footer.ui.compose.IconButton
+import com.android.systemui.qs.panels.ui.viewmodel.toolbar.ToolbarViewModel
+
+@Composable
+fun Toolbar(toolbarViewModelFactory: ToolbarViewModel.Factory, modifier: Modifier = Modifier) {
+ val viewModel = rememberViewModel("Toolbar") { toolbarViewModelFactory.create() }
+
+ Row(
+ modifier = modifier.fillMaxWidth().requiredHeight(48.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ viewModel.userSwitcherViewModel?.let {
+ IconButton(it, Modifier.sysuiResTag("multi_user_switch"))
+ }
+
+ EditModeButton(viewModel.editModeButtonViewModelFactory)
+
+ IconButton(
+ viewModel.settingsButtonViewModel,
+ Modifier.sysuiResTag("settings_button_container"),
+ )
+
+ Spacer(modifier = Modifier.weight(1f))
+ IconButton(viewModel.powerButtonViewModel, Modifier.sysuiResTag("pm_lite"))
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
index 4a18872..3fcb2ab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
@@ -24,6 +24,7 @@
import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QS
import com.android.systemui.qs.panels.domain.interactor.PaginatedGridInteractor
+import com.android.systemui.qs.panels.ui.viewmodel.toolbar.EditModeButtonViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.awaitCancellation
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
index 44dd801..9462321 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
@@ -24,6 +24,7 @@
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.onStart
@Immutable
@@ -37,6 +38,7 @@
awaitClose { tile.removeCallback(callback) }
}
.onStart { emit(tile.state) }
+ .filterNotNull()
.distinctUntilChanged()
val currentState: QSTile.State
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModel.kt
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModel.kt
index b033473..f606218 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModel.kt
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.ui.viewmodel
+package com.android.systemui.qs.panels.ui.viewmodel.toolbar
import com.android.systemui.classifier.domain.interactor.FalsingInteractor
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModel.kt
new file mode 100644
index 0000000..0fde855
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModel.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.panels.ui.viewmodel.toolbar
+
+import android.content.Context
+import android.view.ContextThemeWrapper
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import com.android.systemui.animation.Expandable
+import com.android.systemui.classifier.domain.interactor.FalsingInteractor
+import com.android.systemui.classifier.domain.interactor.runIfNotFalseTap
+import com.android.systemui.globalactions.GlobalActionsDialogLite
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractor
+import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsButtonViewModel
+import com.android.systemui.qs.footer.ui.viewmodel.powerButtonViewModel
+import com.android.systemui.qs.footer.ui.viewmodel.settingsButtonViewModel
+import com.android.systemui.qs.footer.ui.viewmodel.userSwitcherViewModel
+import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import javax.inject.Provider
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+
+class ToolbarViewModel
+@AssistedInject
+constructor(
+ val editModeButtonViewModelFactory: EditModeButtonViewModel.Factory,
+ private val footerActionsInteractor: FooterActionsInteractor,
+ private val globalActionsDialogLiteProvider: Provider<GlobalActionsDialogLite>,
+ private val falsingInteractor: FalsingInteractor,
+ @ShadeDisplayAware appContext: Context,
+) : ExclusiveActivatable() {
+ private val qsThemedContext =
+ ContextThemeWrapper(appContext, R.style.Theme_SystemUI_QuickSettings)
+ private val hydrator = Hydrator("ToolbarViewModel.hydrator")
+
+ val powerButtonViewModel = powerButtonViewModel(qsThemedContext, ::onPowerButtonClicked)
+
+ val settingsButtonViewModel =
+ settingsButtonViewModel(qsThemedContext, ::onSettingsButtonClicked)
+
+ val userSwitcherViewModel: FooterActionsButtonViewModel? by
+ hydrator.hydratedStateOf(
+ traceName = "userSwitcherViewModel",
+ initialValue = null,
+ source =
+ userSwitcherViewModel(
+ qsThemedContext,
+ footerActionsInteractor,
+ ::onUserSwitcherClicked,
+ ),
+ )
+
+ override suspend fun onActivated(): Nothing {
+ coroutineScope {
+ launch {
+ try {
+ globalActionsDialogLite = globalActionsDialogLiteProvider.get()
+ awaitCancellation()
+ } finally {
+ globalActionsDialogLite?.destroy()
+ }
+ }
+ launch { hydrator.activate() }
+ awaitCancellation()
+ }
+ }
+
+ private var globalActionsDialogLite: GlobalActionsDialogLite? by mutableStateOf(null)
+
+ private fun onPowerButtonClicked(expandable: Expandable) {
+ falsingInteractor.runIfNotFalseTap {
+ globalActionsDialogLite?.let {
+ footerActionsInteractor.showPowerMenuDialog(it, expandable)
+ }
+ }
+ }
+
+ private fun onUserSwitcherClicked(expandable: Expandable) {
+ falsingInteractor.runIfNotFalseTap { footerActionsInteractor.showUserSwitcher(expandable) }
+ }
+
+ private fun onSettingsButtonClicked(expandable: Expandable) {
+ falsingInteractor.runIfNotFalseTap { footerActionsInteractor.showSettings(expandable) }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): ToolbarViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 464eeda..1d1e991 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -369,6 +369,7 @@
mHandler.sendEmptyMessage(H.INITIALIZE);
}
+ @androidx.annotation.NonNull
public TState getState() {
return mState;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
index 9c63456..0051bf5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
@@ -109,6 +109,11 @@
userActionInteractor.handleClick(expandable)
}
+ override fun handleSecondaryClick(expandable: Expandable?) = runBlocking {
+ val model = dataInteractor.getCurrentTileModel()
+ userActionInteractor.handleToggleClick(model)
+ }
+
override fun getLongClickIntent(): Intent = userActionInteractor.longClickIntent
@VisibleForTesting
@@ -125,6 +130,7 @@
secondaryLabel = tileState.secondaryLabel
contentDescription = tileState.contentDescription
expandedAccessibilityClassName = tileState.expandedAccessibilityClassName
+ handlesSecondaryClick = true
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt
index 17b78eb..e8c4274 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.base.interactor
import android.annotation.WorkerThread
+import com.android.systemui.plugins.qs.TileDetailsViewModel
interface QSTileUserActionInteractor<DATA_TYPE> {
/**
@@ -27,4 +28,17 @@
* It's safe to run long running computations inside this function.
*/
@WorkerThread suspend fun handleInput(input: QSTileInput<DATA_TYPE>)
+
+ /**
+ * Provides the [TileDetailsViewModel] for constructing the corresponding details view.
+ *
+ * This property is defined here to reuse the business logic. For example, reusing the user
+ * long-click as the go-to-settings callback in the details view.
+ * Subclasses can override this property to provide a specific [TileDetailsViewModel]
+ * implementation.
+ *
+ * @return The [TileDetailsViewModel] instance, or null if not implemented.
+ */
+ val detailsViewModel: TileDetailsViewModel?
+ get() = null
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt
index dde3628..5f476ea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt
@@ -17,18 +17,17 @@
package com.android.systemui.qs.tiles.base.viewmodel
import com.android.systemui.coroutines.newTracingContext
-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.SupervisorJob
/** Creates a [CoroutineScope] for the [QSTileViewModelImpl]. */
class QSTileCoroutineScopeFactory
@Inject
-constructor(@Application private val applicationScope: CoroutineScope) {
+constructor(@Background private val bgDispatcher: CoroutineDispatcher) {
fun create(): CoroutineScope =
- CoroutineScope(
- applicationScope.coroutineContext + SupervisorJob() + newTracingContext("QSTileScope")
- )
+ CoroutineScope(bgDispatcher + SupervisorJob() + newTracingContext("QSTileScope"))
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
index aeb6cef..224fa10 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
@@ -20,6 +20,7 @@
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Dumpable
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.qs.TileDetailsViewModel
import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor
@@ -115,6 +116,9 @@
.flowOn(backgroundDispatcher)
.stateIn(tileScope, SharingStarted.WhileSubscribed(), true)
+ override val detailsViewModel: TileDetailsViewModel?
+ get() = userActionInteractor().detailsViewModel
+
override fun forceUpdate() {
tileScope.launch(context = backgroundDispatcher) { forceUpdates.emit(Unit) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 244f024..dbe1ae9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -149,7 +149,7 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final TelephonyDisplayInfo DEFAULT_TELEPHONY_DISPLAY_INFO =
new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false);
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false, false, false);
static final int MAX_WIFI_ENTRY_COUNT = 3;
@@ -195,8 +195,11 @@
private boolean mHasWifiEntries;
private WifiStateWorker mWifiStateWorker;
private boolean mHasActiveSubIdOnDds;
+ private boolean mIsMobileDataEnabled = false;
@VisibleForTesting
+ Map<Integer, ServiceState> mSubIdServiceState = new HashMap<>();
+ @VisibleForTesting
static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f;
@VisibleForTesting
static final float TOAST_PARAMS_VERTICAL_WEIGHT = 1.0f;
@@ -453,7 +456,7 @@
return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE);
}
- if (mCanConfigWifi && !isMobileDataEnabled()) {
+ if (mCanConfigWifi && !mIsMobileDataEnabled) {
if (DEBUG) {
Log.d(TAG, "Mobile data off");
}
@@ -551,7 +554,7 @@
numLevels += 1;
}
return getSignalStrengthIcon(subId, mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON,
- !isMobileDataEnabled());
+ !mIsMobileDataEnabled);
}
Drawable getSignalStrengthIcon(int subId, Context context, int level, int numLevels,
@@ -681,6 +684,12 @@
// sets the non-DDS to be not found to hide its visual
return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
+ int activeDataSubId = SubscriptionManager.getActiveDataSubscriptionId();
+ if (activeDataSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ || mDefaultDataSubId == activeDataSubId) {
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+
SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(
SubscriptionManager.getActiveDataSubscriptionId());
if (subInfo != null && subInfo.getSubscriptionId() != mDefaultDataSubId
@@ -740,7 +749,6 @@
if (!isMobileDataEnabled()) {
return context.getString(R.string.mobile_data_off_summary);
}
-
String summary = networkTypeDescription;
boolean isForDds = subId == mDefaultDataSubId;
int activeSubId = getActiveAutoSwitchNonDdsSubId();
@@ -753,8 +761,8 @@
context.getString(
isForDds // if nonDds is active, explains Dds status as poor connection
? (isOnNonDds ? R.string.mobile_data_poor_connection
- : R.string.mobile_data_connection_active)
- : R.string.mobile_data_temp_connection_active),
+ : R.string.mobile_data_connection_active)
+ : R.string.mobile_data_temp_connection_active),
networkTypeDescription);
} else if (!isDataStateInService(subId)) {
summary = context.getString(R.string.mobile_data_no_connection);
@@ -963,10 +971,7 @@
* Return {@code true} if mobile data is enabled
*/
boolean isMobileDataEnabled() {
- if (mTelephonyManager == null || !mTelephonyManager.isDataEnabled()) {
- return false;
- }
- return true;
+ return mIsMobileDataEnabled;
}
/**
@@ -1019,8 +1024,8 @@
}
boolean isDataStateInService(int subId) {
- TelephonyManager tm = mSubIdTelephonyManagerMap.getOrDefault(subId, mTelephonyManager);
- final ServiceState serviceState = tm.getServiceState();
+ final ServiceState serviceState = mSubIdServiceState.getOrDefault(subId,
+ new ServiceState());
NetworkRegistrationInfo regInfo =
(serviceState == null) ? null : serviceState.getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS,
@@ -1036,8 +1041,8 @@
return false;
}
- TelephonyManager tm = mSubIdTelephonyManagerMap.getOrDefault(subId, mTelephonyManager);
- final ServiceState serviceState = tm.getServiceState();
+ final ServiceState serviceState = mSubIdServiceState.getOrDefault(subId,
+ new ServiceState());
return serviceState != null
&& serviceState.getState() == serviceState.STATE_IN_SERVICE;
}
@@ -1056,6 +1061,7 @@
final Network activeNetwork = mConnectivityManager.getActiveNetwork();
if (activeNetwork == null) {
+ Log.d(TAG, "getActiveNetwork is null.");
return false;
}
final NetworkCapabilities networkCapabilities =
@@ -1183,14 +1189,16 @@
}
private class InternetTelephonyCallback extends TelephonyCallback implements
+ TelephonyCallback.DataEnabledListener,
TelephonyCallback.DataConnectionStateListener,
TelephonyCallback.DisplayInfoListener,
TelephonyCallback.ServiceStateListener,
TelephonyCallback.SignalStrengthsListener,
TelephonyCallback.UserMobileDataStateListener,
- TelephonyCallback.CarrierNetworkListener{
+ TelephonyCallback.CarrierNetworkListener {
private final int mSubId;
+
private InternetTelephonyCallback(int subId) {
mSubId = subId;
}
@@ -1200,6 +1208,7 @@
if (mCallback != null) {
mCallback.onServiceStateChanged(serviceState);
}
+ mSubIdServiceState.put(mSubId, serviceState);
}
@Override
@@ -1238,6 +1247,13 @@
mCallback.onCarrierNetworkChange(active);
}
}
+
+ @Override
+ public void onDataEnabledChanged(boolean b, int i) {
+ if (mSubId == mDefaultDataSubId) {
+ mIsMobileDataEnabled = b;
+ }
+ }
}
private class InternetOnSubscriptionChangedListener
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
index 0ab533b..378d553 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
@@ -416,9 +416,10 @@
internetContent.mHasEthernet = mInternetDialogController.hasEthernet();
internetContent.mIsWifiEnabled = mInternetDialogController.isWifiEnabled();
internetContent.mHasActiveSubIdOnDds = mInternetDialogController.hasActiveSubIdOnDds();
- internetContent.mIsMobileDataEnabled = mInternetDialogController.isMobileDataEnabled();
internetContent.mIsDeviceLocked = mInternetDialogController.isDeviceLocked();
internetContent.mIsWifiScanEnabled = mInternetDialogController.isWifiScanEnabled();
+ internetContent.mActiveAutoSwitchNonDdsSubId =
+ mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
return internetContent;
}
@@ -433,7 +434,11 @@
private void setOnClickListener(SystemUIDialog dialog) {
mMobileNetworkLayout.setOnClickListener(v -> {
- int autoSwitchNonDdsSubId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
+ int autoSwitchNonDdsSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ if (mDataInternetContent.getValue() != null) {
+ autoSwitchNonDdsSubId =
+ mDataInternetContent.getValue().mActiveAutoSwitchNonDdsSubId;
+ }
if (autoSwitchNonDdsSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
showTurnOffAutoDataSwitchDialog(dialog, autoSwitchNonDdsSubId);
}
@@ -524,7 +529,7 @@
}
} else {
mMobileNetworkLayout.setVisibility(View.VISIBLE);
- mMobileDataToggle.setChecked(internetContent.mIsMobileDataEnabled);
+ mMobileDataToggle.setChecked(mInternetDialogController.isMobileDataEnabled());
mMobileTitleText.setText(getMobileNetworkTitle(mDefaultDataSubId));
String summary = getMobileNetworkSummary(mDefaultDataSubId);
if (!TextUtils.isEmpty(summary)) {
@@ -549,9 +554,9 @@
? R.color.connected_network_primary_color
: R.color.disconnected_network_primary_color;
mMobileToggleDivider.setBackgroundColor(dialog.getContext().getColor(primaryColor));
-
// Display the info for the non-DDS if it's actively being used
- int autoSwitchNonDdsSubId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
+ int autoSwitchNonDdsSubId = internetContent.mActiveAutoSwitchNonDdsSubId;
+
int nonDdsVisibility = autoSwitchNonDdsSubId
!= SubscriptionManager.INVALID_SUBSCRIPTION_ID ? View.VISIBLE : View.GONE;
@@ -983,8 +988,8 @@
boolean mIsCarrierNetworkActive = false;
boolean mIsWifiEnabled = false;
boolean mHasActiveSubIdOnDds = false;
- boolean mIsMobileDataEnabled = false;
boolean mIsDeviceLocked = false;
boolean mIsWifiScanEnabled = false;
+ int mActiveAutoSwitchNonDdsSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
index a963b28..c4f9515 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
@@ -18,10 +18,13 @@
import android.content.Intent
import android.provider.Settings
+import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.qs.TileDetailsViewModel
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.dialog.InternetDetailsViewModel
import com.android.systemui.qs.tiles.dialog.InternetDialogManager
import com.android.systemui.qs.tiles.dialog.WifiStateWorker
import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
@@ -61,11 +64,18 @@
wifiStateWorker.isWifiEnabled = !wifiStateWorker.isWifiEnabled
}
is QSTileUserAction.LongClick -> {
- qsTileIntentUserActionHandler.handle(
- action.expandable,
- Intent(Settings.ACTION_WIFI_SETTINGS)
- )
+ handleLongClick(action.expandable)
}
}
}
+
+ override val detailsViewModel: TileDetailsViewModel =
+ InternetDetailsViewModel { handleLongClick(null) }
+
+ private fun handleLongClick(expandable:Expandable?){
+ qsTileIntentUserActionHandler.handle(
+ expandable,
+ Intent(Settings.ACTION_WIFI_SETTINGS)
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
index eb8b23c..594394f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
@@ -18,13 +18,16 @@
import android.content.Intent
import android.provider.Settings
+import android.util.Log
import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.flags.QSComposeFragment
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.modes.domain.model.ModesTileModel
import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate
import javax.inject.Inject
@@ -35,16 +38,19 @@
private val qsTileIntentUserInputHandler: QSTileIntentUserInputHandler,
// TODO(b/353896370): The domain layer should not have to depend on the UI layer.
private val dialogDelegate: ModesDialogDelegate,
+ private val zenModeInteractor: ZenModeInteractor,
) : QSTileUserActionInteractor<ModesTileModel> {
val longClickIntent = Intent(Settings.ACTION_ZEN_MODE_SETTINGS)
override suspend fun handleInput(input: QSTileInput<ModesTileModel>) {
with(input) {
when (action) {
- is QSTileUserAction.Click,
- is QSTileUserAction.ToggleClick -> {
+ is QSTileUserAction.Click -> {
handleClick(action.expandable)
}
+ is QSTileUserAction.ToggleClick -> {
+ handleToggleClick(input.data)
+ }
is QSTileUserAction.LongClick -> {
qsTileIntentUserInputHandler.handle(action.expandable, longClickIntent)
}
@@ -56,4 +62,29 @@
// Show a dialog with the list of modes to configure.
dialogDelegate.showDialog(expandable)
}
+
+ fun handleToggleClick(modesTileModel: ModesTileModel) {
+ if (QSComposeFragment.isUnexpectedlyInLegacyMode()) {
+ return
+ }
+
+ // If no modes are on, turn on DND since it's the highest-priority mode. Otherwise, turn
+ // them all off.
+ // We want this toggle to work as a shortcut to DND in most cases, but it should still
+ // correctly toggle the tile state to "off" as the user would expect when more modes are on.
+ if (modesTileModel.activeModes.isEmpty()) {
+ val dnd = zenModeInteractor.dndMode.value
+ if (dnd == null) {
+ Log.wtf(TAG, "Triggered DND but it's null!?")
+ return
+ }
+ zenModeInteractor.activateMode(dnd)
+ } else {
+ zenModeInteractor.deactivateAllModes()
+ }
+ }
+
+ companion object {
+ const val TAG = "ModesTileUserActionInteractor"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
index bac048f..1507ef4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
@@ -45,7 +45,11 @@
secondaryLabel = getModesStatus(data, resources)
contentDescription = "$label. $secondaryLabel"
supportedActions =
- setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+ setOf(
+ QSTileState.UserAction.CLICK,
+ QSTileState.UserAction.LONG_CLICK,
+ QSTileState.UserAction.TOGGLE_CLICK,
+ )
sideViewIcon = QSTileState.SideViewIcon.Chevron
expandedAccessibilityClass = Button::class
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
index b1b0001..e8b9926 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.viewmodel
import android.os.UserHandle
+import com.android.systemui.plugins.qs.TileDetailsViewModel
import kotlinx.coroutines.flow.StateFlow
/**
@@ -37,6 +38,10 @@
/** Specifies whether this device currently supports this tile. */
val isAvailable: StateFlow<Boolean>
+ /** Specifies the [TileDetailsViewModel] for constructing the corresponding details view. */
+ val detailsViewModel: TileDetailsViewModel?
+ get() = null
+
/**
* Notifies about the user change. Implementations should avoid using 3rd party userId sources
* and use this value instead. This is to maintain consistent and concurrency-free behaviour
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index f9a1ad5..632eeef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -27,6 +27,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.qs.TileDetailsViewModel
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon
import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
@@ -154,6 +155,10 @@
qsTileViewModel.onUserChanged(UserHandle.of(currentUser))
}
+ override fun getDetailsViewModel(): TileDetailsViewModel? {
+ return qsTileViewModel.detailsViewModel
+ }
+
@Deprecated(
"Not needed as {@link com.android.internal.logging.UiEvent} will use #getMetricsSpec",
replaceWith = ReplaceWith("getMetricsSpec"),
@@ -207,8 +212,9 @@
qsTileViewModel.destroy()
}
- override fun getState(): QSTile.State? =
+ override fun getState(): QSTile.State =
qsTileViewModel.currentState?.let { mapState(context, it, qsTileViewModel.config) }
+ ?: QSTile.State()
override fun getInstanceId(): InstanceId = qsTileViewModel.config.instanceId
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt
index 62b1203..91d9079 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt
@@ -22,6 +22,7 @@
import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.toolbar.ToolbarViewModel
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -38,6 +39,7 @@
val tileGridViewModel: TileGridViewModel,
val editModeViewModel: EditModeViewModel,
val detailsViewModel: DetailsViewModel,
+ val toolbarViewModelFactory: ToolbarViewModel.Factory,
) : ExclusiveActivatable() {
val brightnessSliderViewModel =
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 7d7cab4..c459068 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
@@ -5,7 +5,6 @@
import android.view.MotionEvent
import android.view.View
import android.view.WindowInsets
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
@@ -35,7 +34,6 @@
layoutInsetController: LayoutInsetsController,
sceneDataSourceDelegator: SceneDataSourceDelegator,
qsSceneAdapter: Provider<QSSceneAdapter>,
- alternateBouncerDependencies: AlternateBouncerDependencies,
) {
setLayoutInsetsController(layoutInsetController)
SceneWindowRootViewBinder.bind(
@@ -54,7 +52,6 @@
},
dataSourceDelegator = sceneDataSourceDelegator,
qsSceneAdapter = qsSceneAdapter,
- alternateBouncerDependencies = alternateBouncerDependencies,
)
}
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 1c15c74..f7061d9 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
@@ -36,15 +36,12 @@
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.DisplayCutout
import com.android.systemui.common.ui.compose.windowinsets.ScreenDecorProvider
-import com.android.systemui.keyguard.ui.composable.AlternateBouncer
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.lifecycle.setSnapshotBinding
import com.android.systemui.lifecycle.viewModel
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.res.R
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.scene.ui.composable.Overlay
@@ -77,7 +74,6 @@
onVisibilityChangedInternal: (isVisible: Boolean) -> Unit,
dataSourceDelegator: SceneDataSourceDelegator,
qsSceneAdapter: Provider<QSSceneAdapter>,
- alternateBouncerDependencies: AlternateBouncerDependencies,
) {
val unsortedSceneByKey: Map<SceneKey, Scene> = scenes.associateBy { scene -> scene.key }
val sortedSceneByKey: Map<SceneKey, Scene> =
@@ -148,20 +144,10 @@
// the SceneContainerView. This SharedNotificationContainer should contain NSSL
// due to the NotificationStackScrollLayoutSection (legacy) or
// NotificationSection (scene container) moving it there.
- if (SceneContainerFlag.isEnabled) {
- (sharedNotificationContainer.parent as? ViewGroup)?.removeView(
- sharedNotificationContainer
- )
- view.addView(sharedNotificationContainer)
-
- // TODO(b/358354906): use an overlay for the alternate bouncer
- view.addView(
- createAlternateBouncerView(
- context = view.context,
- alternateBouncerDependencies = alternateBouncerDependencies,
- )
- )
- }
+ (sharedNotificationContainer.parent as? ViewGroup)?.removeView(
+ sharedNotificationContainer
+ )
+ view.addView(sharedNotificationContainer)
view.setSnapshotBinding { onVisibilityChangedInternal(viewModel.isVisible) }
awaitCancellation()
@@ -206,17 +192,6 @@
}
}
- private fun createAlternateBouncerView(
- context: Context,
- alternateBouncerDependencies: AlternateBouncerDependencies,
- ): ComposeView {
- return ComposeView(context).apply {
- setContent {
- AlternateBouncer(alternateBouncerDependencies = alternateBouncerDependencies)
- }
- }
- }
-
// TODO(b/298525212): remove once Compose exposes window inset bounds.
private fun displayCutoutFromWindowInsets(
scope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
index eb568f7..1da4c1d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
@@ -21,7 +21,6 @@
import android.content.Context
import android.content.Intent
import android.hardware.display.DisplayManager
-import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
@@ -30,8 +29,6 @@
import android.view.Display
import android.view.MotionEvent.ACTION_MOVE
import android.view.View
-import android.view.View.GONE
-import android.view.View.VISIBLE
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.AdapterView
import android.widget.ArrayAdapter
@@ -44,10 +41,10 @@
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
import com.android.systemui.mediaprojection.permission.BaseMediaProjectionPermissionDialogDelegate
+import com.android.systemui.mediaprojection.permission.BaseMediaProjectionPermissionViewBinder
import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
import com.android.systemui.mediaprojection.permission.SINGLE_APP
import com.android.systemui.mediaprojection.permission.ScreenShareMode
-import com.android.systemui.mediaprojection.permission.ScreenShareOption
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.settings.UserContextProvider
@@ -64,15 +61,15 @@
private val activityStarter: ActivityStarter,
private val userContextProvider: UserContextProvider,
private val onStartRecordingClicked: Runnable?,
- mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
+ private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
private val systemUIDialogFactory: SystemUIDialog.Factory,
@ScreenShareMode defaultSelectedMode: Int,
@StyleRes private val theme: Int,
private val context: Context,
- displayManager: DisplayManager,
+ private val displayManager: DisplayManager,
) :
BaseMediaProjectionPermissionDialogDelegate<SystemUIDialog>(
- createOptionList(displayManager),
+ ScreenRecordPermissionViewBinder.createOptionList(displayManager),
appName = null,
hostUid = hostUid,
mediaProjectionMetricsLogger,
@@ -119,10 +116,19 @@
}
private lateinit var tapsSwitch: Switch
- private lateinit var tapsView: View
private lateinit var audioSwitch: Switch
private lateinit var options: Spinner
+ override fun createViewBinder(): BaseMediaProjectionPermissionViewBinder {
+ return ScreenRecordPermissionViewBinder(
+ hostUid,
+ mediaProjectionMetricsLogger,
+ defaultSelectedMode,
+ displayManager,
+ dialog,
+ )
+ }
+
override fun createDialog(): SystemUIDialog {
return systemUIDialogFactory.create(this, context, theme)
}
@@ -133,6 +139,7 @@
dialog.setTitle(R.string.screenrecord_title)
setStartButtonOnClickListener { v: View? ->
onStartRecordingClicked?.run()
+ val selectedScreenShareOption = getSelectedScreenShareOption()
if (selectedScreenShareOption.mode == ENTIRE_SCREEN) {
requestScreenCapture(/* captureTarget= */ null, selectedScreenShareOption.displayId)
}
@@ -168,6 +175,7 @@
@SuppressLint("ClickableViewAccessibility")
private fun initRecordOptionsView() {
+ // TODO(b/378514312): Move this function to ScreenRecordPermissionViewBinder
audioSwitch = dialog.requireViewById(R.id.screenrecord_audio_switch)
tapsSwitch = dialog.requireViewById(R.id.screenrecord_taps_switch)
@@ -176,9 +184,6 @@
audioSwitch.setOnTouchListener { _, event -> event.action == ACTION_MOVE }
tapsSwitch.setOnTouchListener { _, event -> event.action == ACTION_MOVE }
- tapsView = dialog.requireViewById(R.id.show_taps)
- updateTapsViewVisibility()
-
options = dialog.requireViewById(R.id.screen_recording_options)
val a: ArrayAdapter<*> =
ScreenRecordingAdapter(
@@ -206,15 +211,6 @@
options.isLongClickable = false
}
- override fun onItemSelected(adapterView: AdapterView<*>?, view: View, pos: Int, id: Long) {
- super.onItemSelected(adapterView, view, pos, id)
- updateTapsViewVisibility()
- }
-
- private fun updateTapsViewVisibility() {
- tapsView.visibility = if (selectedScreenShareOption.mode == SINGLE_APP) GONE else VISIBLE
- }
-
/**
* Starts screen capture after some countdown
*
@@ -226,7 +222,7 @@
displayId: Int = Display.DEFAULT_DISPLAY,
) {
val userContext = userContextProvider.userContext
- val showTaps = selectedScreenShareOption.mode != SINGLE_APP && tapsSwitch.isChecked
+ val showTaps = getSelectedScreenShareOption().mode != SINGLE_APP && tapsSwitch.isChecked
val audioMode =
if (audioSwitch.isChecked) options.selectedItem as ScreenRecordingAudioSource
else ScreenRecordingAudioSource.NONE
@@ -279,81 +275,5 @@
)
private const val DELAY_MS: Long = 3000
private const val INTERVAL_MS: Long = 1000
-
- private val RECORDABLE_DISPLAY_TYPES =
- intArrayOf(
- Display.TYPE_OVERLAY,
- Display.TYPE_EXTERNAL,
- Display.TYPE_INTERNAL,
- Display.TYPE_WIFI,
- )
-
- private val filterDeviceTypeFlag: Boolean =
- com.android.media.projection.flags.Flags
- .mediaProjectionConnectedDisplayNoVirtualDevice()
-
- private fun createOptionList(displayManager: DisplayManager): List<ScreenShareOption> {
- if (!com.android.media.projection.flags.Flags.mediaProjectionConnectedDisplay()) {
- return listOf(
- ScreenShareOption(
- SINGLE_APP,
- R.string.screenrecord_permission_dialog_option_text_single_app,
- R.string.screenrecord_permission_dialog_warning_single_app,
- startButtonText =
- R.string
- .media_projection_entry_generic_permission_dialog_continue_single_app,
- ),
- ScreenShareOption(
- ENTIRE_SCREEN,
- R.string.screenrecord_permission_dialog_option_text_entire_screen,
- R.string.screenrecord_permission_dialog_warning_entire_screen,
- startButtonText =
- R.string.screenrecord_permission_dialog_continue_entire_screen,
- displayId = Display.DEFAULT_DISPLAY,
- displayName = Build.MODEL,
- ),
- )
- }
-
- return listOf(
- ScreenShareOption(
- SINGLE_APP,
- R.string.screenrecord_permission_dialog_option_text_single_app,
- R.string.screenrecord_permission_dialog_warning_single_app,
- startButtonText =
- R.string
- .media_projection_entry_generic_permission_dialog_continue_single_app,
- ),
- ScreenShareOption(
- ENTIRE_SCREEN,
- R.string.screenrecord_permission_dialog_option_text_entire_screen_for_display,
- R.string.screenrecord_permission_dialog_warning_entire_screen,
- startButtonText =
- R.string.screenrecord_permission_dialog_continue_entire_screen,
- displayId = Display.DEFAULT_DISPLAY,
- displayName = Build.MODEL,
- ),
- ) +
- displayManager.displays
- .filter {
- it.displayId != Display.DEFAULT_DISPLAY &&
- (!filterDeviceTypeFlag || it.type in RECORDABLE_DISPLAY_TYPES)
- }
- .map {
- ScreenShareOption(
- ENTIRE_SCREEN,
- R.string
- .screenrecord_permission_dialog_option_text_entire_screen_for_display,
- warningText =
- R.string
- .media_projection_entry_app_permission_dialog_warning_entire_screen,
- startButtonText =
- R.string
- .media_projection_entry_app_permission_dialog_continue_entire_screen,
- displayId = it.displayId,
- displayName = it.name,
- )
- }
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt
new file mode 100644
index 0000000..91c6b47
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.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.screenrecord
+
+import android.annotation.SuppressLint
+import android.app.AlertDialog
+import android.hardware.display.DisplayManager
+import android.os.Build
+import android.view.Display
+import android.view.View
+import android.view.View.GONE
+import android.view.View.VISIBLE
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
+import com.android.systemui.mediaprojection.permission.BaseMediaProjectionPermissionViewBinder
+import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
+import com.android.systemui.mediaprojection.permission.SINGLE_APP
+import com.android.systemui.mediaprojection.permission.ScreenShareMode
+import com.android.systemui.mediaprojection.permission.ScreenShareOption
+import com.android.systemui.res.R
+
+class ScreenRecordPermissionViewBinder(
+ hostUid: Int,
+ mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
+ @ScreenShareMode defaultSelectedMode: Int,
+ displayManager: DisplayManager,
+ private val dialog: AlertDialog,
+) :
+ BaseMediaProjectionPermissionViewBinder(
+ createOptionList(displayManager),
+ appName = null,
+ hostUid = hostUid,
+ mediaProjectionMetricsLogger,
+ defaultSelectedMode,
+ dialog,
+ ) {
+ private lateinit var tapsView: View
+
+ override fun bind() {
+ super.bind()
+ initRecordOptionsView()
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ private fun initRecordOptionsView() {
+ tapsView = dialog.requireViewById(R.id.show_taps)
+ updateTapsViewVisibility()
+ }
+
+ override fun onItemSelected(pos: Int) {
+ super.onItemSelected(pos)
+ updateTapsViewVisibility()
+ }
+
+ private fun updateTapsViewVisibility() {
+ tapsView.visibility = if (selectedScreenShareOption.mode == SINGLE_APP) GONE else VISIBLE
+ }
+
+ companion object {
+ private val RECORDABLE_DISPLAY_TYPES =
+ intArrayOf(
+ Display.TYPE_OVERLAY,
+ Display.TYPE_EXTERNAL,
+ Display.TYPE_INTERNAL,
+ Display.TYPE_WIFI,
+ )
+
+ private val filterDeviceTypeFlag: Boolean =
+ com.android.media.projection.flags.Flags
+ .mediaProjectionConnectedDisplayNoVirtualDevice()
+
+ fun createOptionList(displayManager: DisplayManager): List<ScreenShareOption> {
+ if (!com.android.media.projection.flags.Flags.mediaProjectionConnectedDisplay()) {
+ return listOf(
+ ScreenShareOption(
+ SINGLE_APP,
+ R.string.screenrecord_permission_dialog_option_text_single_app,
+ R.string.screenrecord_permission_dialog_warning_single_app,
+ startButtonText =
+ R.string
+ .media_projection_entry_generic_permission_dialog_continue_single_app,
+ ),
+ ScreenShareOption(
+ ENTIRE_SCREEN,
+ R.string.screenrecord_permission_dialog_option_text_entire_screen,
+ R.string.screenrecord_permission_dialog_warning_entire_screen,
+ startButtonText =
+ R.string.screenrecord_permission_dialog_continue_entire_screen,
+ displayId = Display.DEFAULT_DISPLAY,
+ displayName = Build.MODEL,
+ ),
+ )
+ }
+
+ return listOf(
+ ScreenShareOption(
+ SINGLE_APP,
+ R.string.screenrecord_permission_dialog_option_text_single_app,
+ R.string.screenrecord_permission_dialog_warning_single_app,
+ startButtonText =
+ R.string
+ .media_projection_entry_generic_permission_dialog_continue_single_app,
+ ),
+ ScreenShareOption(
+ ENTIRE_SCREEN,
+ R.string.screenrecord_permission_dialog_option_text_entire_screen_for_display,
+ R.string.screenrecord_permission_dialog_warning_entire_screen,
+ startButtonText =
+ R.string.screenrecord_permission_dialog_continue_entire_screen,
+ displayId = Display.DEFAULT_DISPLAY,
+ displayName = Build.MODEL,
+ ),
+ ) +
+ displayManager.displays
+ .filter {
+ it.displayId != Display.DEFAULT_DISPLAY &&
+ (!filterDeviceTypeFlag || it.type in RECORDABLE_DISPLAY_TYPES)
+ }
+ .map {
+ ScreenShareOption(
+ ENTIRE_SCREEN,
+ R.string
+ .screenrecord_permission_dialog_option_text_entire_screen_for_display,
+ warningText =
+ R.string
+ .media_projection_entry_app_permission_dialog_warning_entire_screen,
+ startButtonText =
+ R.string
+ .media_projection_entry_app_permission_dialog_continue_entire_screen,
+ displayId = it.displayId,
+ displayName = it.name,
+ )
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index 7b01c36..a365b7c5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -37,7 +37,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.screenshot.proxy.SystemUiProxy
+import com.android.systemui.screenshot.proxy.ScreenshotProxy
import com.android.systemui.settings.DisplayTracker
import com.android.systemui.shared.system.ActivityManagerWrapper
import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -55,7 +55,7 @@
private val activityManagerWrapper: ActivityManagerWrapper,
@Application private val applicationScope: CoroutineScope,
@Main private val mainDispatcher: CoroutineDispatcher,
- private val systemUiProxy: SystemUiProxy,
+ private val screenshotProxy: ScreenshotProxy,
private val displayTracker: DisplayTracker,
) {
/**
@@ -89,7 +89,7 @@
CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT
)
}
- systemUiProxy.dismissKeyguard()
+ screenshotProxy.dismissKeyguard()
var transitionOptions: ActivityOptions? = null
if (transitionCoordinator?.decor?.isAttachedToWindow == true) {
transitionCoordinator.startExit()
@@ -127,7 +127,7 @@
private suspend fun launchCrossProfileIntent(
user: UserHandle,
intent: Intent,
- bundle: Bundle?
+ bundle: Bundle?,
) {
val connector = getCrossProfileConnector(user)
val completion = CompletableDeferred<Unit>()
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 90695fa..253e3a6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -38,7 +38,7 @@
import com.android.systemui.screenshot.appclips.AppClipsService;
import com.android.systemui.screenshot.message.MessageModule;
import com.android.systemui.screenshot.policy.ScreenshotPolicyModule;
-import com.android.systemui.screenshot.proxy.SystemUiProxyModule;
+import com.android.systemui.screenshot.proxy.ScreenshotProxyModule;
import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel;
import dagger.Binds;
@@ -50,7 +50,7 @@
/**
* Defines injectable resources for Screenshots
*/
-@Module(includes = {ScreenshotPolicyModule.class, SystemUiProxyModule.class, MessageModule.class})
+@Module(includes = {ScreenshotPolicyModule.class, ScreenshotProxyModule.class, MessageModule.class})
public abstract class ScreenshotModule {
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepositoryImpl.kt
index e9599dc..ec34808 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepositoryImpl.kt
@@ -22,21 +22,21 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.screenshot.data.model.DisplayContentModel
import com.android.systemui.screenshot.data.model.SystemUiState
-import com.android.systemui.screenshot.proxy.SystemUiProxy
+import com.android.systemui.screenshot.proxy.ScreenshotProxy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
/**
* Implements DisplayTaskRepository using [IActivityTaskManager], along with [ProfileTypeRepository]
- * and [SystemUiProxy].
+ * and [ScreenshotProxy].
*/
@SuppressLint("MissingPermission")
class DisplayContentRepositoryImpl
@Inject
constructor(
private val atmService: IActivityTaskManager,
- private val systemUiProxy: SystemUiProxy,
+ private val screenshotProxy: ScreenshotProxy,
@Background private val background: CoroutineDispatcher,
) : DisplayContentRepository {
@@ -53,8 +53,8 @@
): DisplayContentModel {
return DisplayContentModel(
displayId,
- SystemUiState(systemUiProxy.isNotificationShadeExpanded()),
- rootTasks
+ SystemUiState(screenshotProxy.isNotificationShadeExpanded()),
+ rootTasks,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/IOnDoneCallback.aidl b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/IOnDoneCallback.aidl
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/screenshot/IOnDoneCallback.aidl
rename to packages/SystemUI/src/com/android/systemui/screenshot/proxy/IOnDoneCallback.aidl
index e15030f..fb97fa7 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/IOnDoneCallback.aidl
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/IOnDoneCallback.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.proxy;
interface IOnDoneCallback {
void onDone(boolean success);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/IScreenshotProxy.aidl b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/IScreenshotProxy.aidl
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/screenshot/IScreenshotProxy.aidl
rename to packages/SystemUI/src/com/android/systemui/screenshot/proxy/IScreenshotProxy.aidl
index d2e3fbd..7b2f7e6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/IScreenshotProxy.aidl
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/IScreenshotProxy.aidl
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.proxy;
-import com.android.systemui.screenshot.IOnDoneCallback;
+import com.android.systemui.screenshot.proxy.IOnDoneCallback;
/** Interface implemented by ScreenshotProxyService */
interface IScreenshotProxy {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxy.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxy.kt
rename to packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxy.kt
index e3eb3c4..b44168f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxy.kt
@@ -24,7 +24,7 @@
*
* TODO: Rename and relocate 'ScreenshotProxyService' to this package and remove duplicate clients.
*/
-interface SystemUiProxy {
+interface ScreenshotProxy {
/** Indicate if the notification shade is "open"... (not in the fully collapsed position) */
suspend fun isNotificationShadeExpanded(): Boolean
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxyClient.kt b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyClient.kt
similarity index 85%
rename from packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxyClient.kt
rename to packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyClient.kt
index dcf58bd..1158e2e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxyClient.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyClient.kt
@@ -22,9 +22,6 @@
import android.util.Log
import com.android.internal.infra.ServiceConnector
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.screenshot.IOnDoneCallback
-import com.android.systemui.screenshot.IScreenshotProxy
-import com.android.systemui.screenshot.ScreenshotProxyService
import javax.inject.Inject
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
@@ -32,8 +29,8 @@
private const val TAG = "SystemUiProxy"
-/** An implementation of [SystemUiProxy] using [ScreenshotProxyService]. */
-class SystemUiProxyClient @Inject constructor(@Application context: Context) : SystemUiProxy {
+/** An implementation of [ScreenshotProxy] using [ScreenshotProxyService]. */
+class ScreenshotProxyClient @Inject constructor(@Application context: Context) : ScreenshotProxy {
@SuppressLint("ImplicitSamInstance")
private val proxyConnector: ServiceConnector<IScreenshotProxy> =
ServiceConnector.Impl(
@@ -41,7 +38,7 @@
Intent(context, ScreenshotProxyService::class.java),
Context.BIND_AUTO_CREATE or Context.BIND_WAIVE_PRIORITY or Context.BIND_NOT_VISIBLE,
context.userId,
- IScreenshotProxy.Stub::asInterface
+ IScreenshotProxy.Stub::asInterface,
)
override suspend fun isNotificationShadeExpanded(): Boolean = suspendCoroutine { k ->
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxyModule.kt b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyModule.kt
similarity index 85%
rename from packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxyModule.kt
rename to packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyModule.kt
index 4dd5cc4..797be91 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxyModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyModule.kt
@@ -18,14 +18,13 @@
import android.app.Service
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.screenshot.ScreenshotProxyService
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
@Module
-interface SystemUiProxyModule {
+interface ScreenshotProxyModule {
@Binds
@IntoMap
@@ -34,5 +33,5 @@
@Binds
@SysUISingleton
- fun bindSystemUiProxy(systemUiProxyClient: SystemUiProxyClient): SystemUiProxy
+ fun bindSystemUiProxy(screenshotProxyClient: ScreenshotProxyClient): ScreenshotProxy
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyService.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
rename to packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyService.kt
index 6df22f0..a84f552 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyService.kt
@@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.screenshot
+
+package com.android.systemui.screenshot.proxy
import android.content.Intent
import android.os.IBinder
@@ -67,7 +68,7 @@
null,
true /* dismissShade */,
true /* afterKeyguardGone */,
- true /* deferred */
+ true, /* deferred */
)
}
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 c5c705c..4ad222d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
@@ -162,7 +162,12 @@
)
if (cutout == null) {
- screenshotStatic.setPadding(0, 0, 0, navBarInsets.bottom)
+ screenshotStatic.setPadding(
+ navBarInsets.left,
+ navBarInsets.top,
+ navBarInsets.right,
+ navBarInsets.bottom,
+ )
} else {
val waterfall = cutout.waterfallInsets
if (inPortrait) {
@@ -179,9 +184,9 @@
)
} else {
screenshotStatic.setPadding(
- max(cutout.safeInsetLeft, waterfall.left),
+ max(cutout.safeInsetLeft, waterfall.left, navBarInsets.left),
waterfall.top,
- max(cutout.safeInsetRight, waterfall.right),
+ max(cutout.safeInsetRight, waterfall.right, navBarInsets.right),
max(
navBarInsets.bottom + verticalPadding,
waterfall.bottom + verticalPadding,
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
index 9a1ffcb..3c03d28 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
@@ -35,6 +35,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import static com.android.systemui.Flags.notificationShadeBlur;
/**
* Drawable used on SysUI scrims.
@@ -213,6 +214,10 @@
public void draw(@NonNull Canvas canvas) {
mPaint.setColor(mMainColor);
mPaint.setAlpha(mAlpha);
+ if (notificationShadeBlur()) {
+ // TODO(b/370555223): Match the alpha to the visual spec when it is finalized.
+ mPaint.setAlpha((int) (0.5f * mAlpha));
+ }
if (mConcaveInfo != null) {
drawConcave(canvas);
} else if (mCornerRadiusEnabled && mCornerRadius > 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
index 49f3cfc..4bfa61e 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
@@ -44,6 +44,8 @@
import java.util.concurrent.Executor;
+import static com.android.systemui.Flags.notificationShadeBlur;
+
/**
* A view which can draw a scrim. This view maybe be used in multiple windows running on different
* threads, but is controlled by {@link com.android.systemui.statusbar.phone.ScrimController} so we
@@ -250,6 +252,10 @@
if (mBlendWithMainColor) {
mainTinted = ColorUtils.blendARGB(mColors.getMainColor(), mTintColor, tintAmount);
}
+ if (notificationShadeBlur()) {
+ // TODO(b/370555223): Fix color and transparency to match visual spec exactly
+ mainTinted = ColorUtils.blendARGB(mColors.getMainColor(), Color.GRAY, 0.5f);
+ }
drawable.setColor(mainTinted, animated);
} else {
boolean hasAlpha = Color.alpha(mTintColor) != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 31780a5..61ac1a02 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -41,11 +41,11 @@
import com.android.compose.theme.PlatformTheme
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.Flags
-import com.android.systemui.Flags.communalHubOnMobile
import com.android.systemui.ambient.touch.TouchMonitor
import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
import com.android.systemui.communal.dagger.Communal
import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.ui.compose.CommunalContainer
import com.android.systemui.communal.ui.compose.CommunalContent
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
@@ -83,6 +83,7 @@
@Inject
constructor(
private val communalInteractor: CommunalInteractor,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
private val communalViewModel: CommunalViewModel,
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
@@ -514,7 +515,7 @@
val touchOnUmo = keyguardMediaController.isWithinMediaViewBounds(ev.x.toInt(), ev.y.toInt())
val touchOnSmartspace =
lockscreenSmartspaceController.isWithinSmartspaceBounds(ev.x.toInt(), ev.y.toInt())
- val glanceableHubV2 = communalHubOnMobile()
+ val glanceableHubV2 = communalSettingsInteractor.isV2FlagEnabled()
if (
!hubShowing &&
(touchOnNotifications || touchOnUmo || touchOnSmartspace || glanceableHubV2)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index 15b2270..8937ce3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -33,7 +33,6 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.res.R
@@ -91,7 +90,6 @@
layoutInsetController: NotificationInsetsController,
sceneDataSourceDelegator: Provider<SceneDataSourceDelegator>,
qsSceneAdapter: Provider<QSSceneAdapter>,
- alternateBouncerDependencies: Provider<AlternateBouncerDependencies>,
): WindowRootView {
return if (SceneContainerFlag.isEnabled) {
checkNoSceneDuplicates(scenesProvider.get())
@@ -107,7 +105,6 @@
layoutInsetController = layoutInsetController,
sceneDataSourceDelegator = sceneDataSourceDelegator.get(),
qsSceneAdapter = qsSceneAdapter,
- alternateBouncerDependencies = alternateBouncerDependencies.get(),
)
sceneWindowRootView
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt
index dd1b58c..c6752f8 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt
@@ -30,8 +30,8 @@
isDownFromTopEdgeEnabled: Boolean = true,
requireTwoPointersForTopEdgeForQs: Boolean = false,
): Array<Pair<UserAction, UserActionResult>> {
- val shadeUserActionResult = UserActionResult(Scenes.Shade, isIrreversible = true)
- val qsSceneUserActionResult = UserActionResult(Scenes.QuickSettings, isIrreversible = true)
+ val shadeUserActionResult = UserActionResult(Scenes.Shade)
+ val qsSceneUserActionResult = UserActionResult(Scenes.QuickSettings)
return buildList {
// Swiping down, not from the edge, always goes to shade.
add(Swipe.Down to shadeUserActionResult)
@@ -53,7 +53,7 @@
/** Returns collection of [UserAction] to [UserActionResult] pairs for opening the split shade. */
fun splitShadeActions(): Array<Pair<UserAction, UserActionResult>> {
- val shadeUserActionResult = UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true)
+ val shadeUserActionResult = UserActionResult(Scenes.Shade, ToSplitShade)
return arrayOf(
// Swiping down, not from the edge, always goes to shade.
Swipe.Down to shadeUserActionResult,
@@ -66,10 +66,8 @@
/** Returns collection of [UserAction] to [UserActionResult] pairs for opening the dual shade. */
fun dualShadeActions(): Array<Pair<UserAction, UserActionResult>> {
- val notifShadeUserActionResult =
- UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true)
- val qsShadeuserActionResult =
- UserActionResult.ShowOverlay(Overlays.QuickSettingsShade, isIrreversible = true)
+ val notifShadeUserActionResult = UserActionResult.ShowOverlay(Overlays.NotificationsShade)
+ val qsShadeuserActionResult = UserActionResult.ShowOverlay(Overlays.QuickSettingsShade)
return arrayOf(
Swipe.Down to notifShadeUserActionResult,
Swipe.Down(fromSource = SceneContainerEdge.TopRight) to qsShadeuserActionResult,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index 6bbd4a5..d343ed5a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -35,6 +35,7 @@
import com.android.systemui.dump.DumpManager
import java.io.PrintWriter
import javax.inject.Inject
+import com.android.systemui.Flags.notificationShadeBlur
@SysUISingleton
open class BlurUtils @Inject constructor(
@@ -43,7 +44,14 @@
dumpManager: DumpManager
) : Dumpable {
val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius)
- val maxBlurRadius = resources.getDimensionPixelSize(R.dimen.max_window_blur_radius)
+ val maxBlurRadius =
+ if (notificationShadeBlur()) {
+ resources.getDimensionPixelSize(R.dimen.max_shade_window_blur_radius)
+ } else {
+ resources.getDimensionPixelSize(R.dimen.max_window_blur_radius)
+ }
+
+
private var lastAppliedBlur = 0
private var earlyWakeupEnabled = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index c997ac5..6fd2d3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -768,8 +768,6 @@
Intent.CATEGORY_APP_EMAIL,
Intent.CATEGORY_APP_CALENDAR,
Intent.CATEGORY_APP_MAPS,
- Intent.CATEGORY_APP_MUSIC,
- Intent.CATEGORY_APP_MESSAGING,
Intent.CATEGORY_APP_CALCULATOR,
};
String[] shortcutLabels = {
@@ -778,19 +776,15 @@
mContext.getString(R.string.keyboard_shortcut_group_applications_email),
mContext.getString(R.string.keyboard_shortcut_group_applications_calendar),
mContext.getString(R.string.keyboard_shortcut_group_applications_maps),
- mContext.getString(R.string.keyboard_shortcut_group_applications_music),
- mContext.getString(R.string.keyboard_shortcut_group_applications_sms),
mContext.getString(R.string.keyboard_shortcut_group_applications_calculator)
};
int[] keyCodes = {
KeyEvent.KEYCODE_B,
- KeyEvent.KEYCODE_C,
- KeyEvent.KEYCODE_E,
- KeyEvent.KEYCODE_K,
- KeyEvent.KEYCODE_M,
KeyEvent.KEYCODE_P,
- KeyEvent.KEYCODE_S,
+ KeyEvent.KEYCODE_E,
+ KeyEvent.KEYCODE_C,
+ KeyEvent.KEYCODE_M,
KeyEvent.KEYCODE_U,
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 1db7fb4..684466a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -53,6 +53,7 @@
import javax.inject.Inject
import kotlin.math.max
import kotlin.math.sign
+import com.android.systemui.Flags.notificationShadeBlur
/**
* Responsible for blurring the notification shade window, and applying a zoom effect to the
@@ -220,7 +221,9 @@
// Make blur be 0 if it is necessary to stop blur effect.
if (scrimsVisible) {
- blur = 0
+ if (!notificationShadeBlur()) {
+ blur = 0
+ }
zoomOut = 0f
}
@@ -240,7 +243,7 @@
Choreographer.FrameCallback {
updateScheduled = false
val (blur, zoomOut) = computeBlurAndZoomOut()
- val opaque = scrimsVisible && !blursDisabledForAppLaunch
+ val opaque = if (notificationShadeBlur()) false else scrimsVisible && !blursDisabledForAppLaunch
Trace.traceCounter(Trace.TRACE_TAG_APP, "shade_blur_radius", blur)
blurUtils.applyBlur(root.viewRootImpl, blur, opaque)
lastAppliedBlur = blur
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
index 6f491e7..85b50d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
@@ -35,6 +35,7 @@
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -67,9 +68,17 @@
Flags.statusBarCallChipNotificationIcon() &&
state.notificationIconView != null
) {
+ StatusBarConnectedDisplays.assertInLegacyMode()
OngoingActivityChipModel.ChipIcon.StatusBarView(
state.notificationIconView
)
+ } else if (
+ StatusBarConnectedDisplays.isEnabled &&
+ Flags.statusBarCallChipNotificationIcon()
+ ) {
+ OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon(
+ state.notificationKey
+ )
} else {
OngoingActivityChipModel.ChipIcon.SingleColorIcon(phoneIcon)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
index c57c807..571a3e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
@@ -22,6 +22,7 @@
import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.chips.notification.domain.model.NotificationChipModel
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -100,10 +101,15 @@
private fun ActiveNotificationModel.toNotificationChipModel(): NotificationChipModel? {
val statusBarChipIconView = this.statusBarChipIconView
if (statusBarChipIconView == null) {
- logger.w({ "$str1: Can't show chip because status bar chip icon view is null" }) {
- str1 = extraLogTag
+ if (!StatusBarConnectedDisplays.isEnabled) {
+ logger.w({ "$str1: Can't show chip because status bar chip icon view is null" }) {
+ str1 = extraLogTag
+ }
+ // When the flag is disabled, we keep the old behavior of returning null.
+ // When the flag is enabled, the icon will always be null, and will later be
+ // fetched in the UI layer using the notification key.
+ return null
}
- return null
}
return NotificationChipModel(key, statusBarChipIconView, whenTime)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
index bc4241d..4588b19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
@@ -21,6 +21,6 @@
/** Modeling all the data needed to render a status bar notification chip. */
data class NotificationChipModel(
val key: String,
- val statusBarChipIconView: StatusBarIconView,
+ val statusBarChipIconView: StatusBarIconView?,
val whenTime: Long,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
index b2f7e2f..2cd5bb3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -24,6 +24,7 @@
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -50,7 +51,14 @@
/** Converts the notification to the [OngoingActivityChipModel] object. */
private fun NotificationChipModel.toActivityChipModel(): OngoingActivityChipModel.Shown {
StatusBarNotifChips.assertInNewMode()
- val icon = OngoingActivityChipModel.ChipIcon.StatusBarView(this.statusBarChipIconView)
+ val icon =
+ if (this.statusBarChipIconView != null) {
+ StatusBarConnectedDisplays.assertInLegacyMode()
+ OngoingActivityChipModel.ChipIcon.StatusBarView(this.statusBarChipIconView)
+ } else {
+ StatusBarConnectedDisplays.assertInNewMode()
+ OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon(this.key)
+ }
// TODO(b/364653005): Use the notification color if applicable.
val colors = ColorsModel.Themed
val onClickListener =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
index 730784a..cf69d40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
@@ -32,11 +32,13 @@
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
/** Binder for ongoing activity chip views. */
object OngoingActivityChipBinder {
/** Binds the given [chipModel] data to the given [chipView]. */
- fun bind(chipModel: OngoingActivityChipModel, chipView: View) {
+ fun bind(chipModel: OngoingActivityChipModel, chipView: View, iconViewStore: IconViewStore?) {
val chipContext = chipView.context
val chipDefaultIconView: ImageView =
chipView.requireViewById(R.id.ongoing_activity_chip_icon)
@@ -51,7 +53,7 @@
when (chipModel) {
is OngoingActivityChipModel.Shown -> {
// Data
- setChipIcon(chipModel, chipBackgroundView, chipDefaultIconView)
+ setChipIcon(chipModel, chipBackgroundView, chipDefaultIconView, iconViewStore)
setChipMainContent(chipModel, chipTextView, chipTimeView, chipShortTimeDeltaView)
chipView.setOnClickListener(chipModel.onClickListener)
updateChipPadding(
@@ -85,6 +87,7 @@
chipModel: OngoingActivityChipModel.Shown,
backgroundView: ChipBackgroundContainer,
defaultIconView: ImageView,
+ iconViewStore: IconViewStore?,
) {
// Always remove any previously set custom icon. If we have a new custom icon, we'll re-add
// it.
@@ -108,40 +111,64 @@
defaultIconView.untintView()
}
is OngoingActivityChipModel.ChipIcon.StatusBarView -> {
- // Hide the default icon since we'll show this custom icon instead.
- defaultIconView.visibility = View.GONE
-
- // Add the new custom icon:
- // 1. Set up the right visual params.
- val iconView = icon.impl
- with(iconView) {
- id = CUSTOM_ICON_VIEW_ID
- // TODO(b/354930838): Update the content description to not include "phone" and
- // maybe include the app name.
- contentDescription =
- context.resources.getString(R.string.ongoing_phone_call_content_description)
- tintView(iconTint)
+ StatusBarConnectedDisplays.assertInLegacyMode()
+ setStatusBarIconView(defaultIconView, icon.impl, iconTint, backgroundView)
+ }
+ is OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon -> {
+ StatusBarConnectedDisplays.assertInNewMode()
+ val iconView = fetchStatusBarIconView(iconViewStore, icon)
+ if (iconView == null) {
+ // This means that the notification key doesn't exist anymore.
+ return
}
-
- // 2. If we just reinflated the view, we may need to detach the icon view from the
- // old chip before we reattach it to the new one.
- // See also: NotificationIconContainerViewBinder#bindIcons.
- val currentParent = iconView.parent as? ViewGroup
- if (currentParent != null && currentParent != backgroundView) {
- currentParent.removeView(iconView)
- currentParent.removeTransientView(iconView)
- }
-
- // 3: Add the icon as the starting view.
- backgroundView.addView(
- iconView,
- /* index= */ 0,
- generateCustomIconLayoutParams(iconView),
- )
+ setStatusBarIconView(defaultIconView, iconView, iconTint, backgroundView)
}
}
}
+ private fun fetchStatusBarIconView(
+ iconViewStore: IconViewStore?,
+ icon: OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon,
+ ): StatusBarIconView? {
+ StatusBarConnectedDisplays.assertInNewMode()
+ if (iconViewStore == null) {
+ throw IllegalStateException("Store should always be non-null when flag is enabled.")
+ }
+ return iconViewStore.iconView(icon.notificationKey)
+ }
+
+ private fun setStatusBarIconView(
+ defaultIconView: ImageView,
+ iconView: StatusBarIconView,
+ iconTint: Int,
+ backgroundView: ChipBackgroundContainer,
+ ) {
+ // Hide the default icon since we'll show this custom icon instead.
+ defaultIconView.visibility = View.GONE
+
+ // 1. Set up the right visual params.
+ with(iconView) {
+ id = CUSTOM_ICON_VIEW_ID
+ // TODO(b/354930838): Update the content description to not include "phone" and maybe
+ // include the app name.
+ contentDescription =
+ context.resources.getString(R.string.ongoing_phone_call_content_description)
+ tintView(iconTint)
+ }
+
+ // 2. If we just reinflated the view, we may need to detach the icon view from the old chip
+ // before we reattach it to the new one.
+ // See also: NotificationIconContainerViewBinder#bindIcons.
+ val currentParent = iconView.parent as? ViewGroup
+ if (currentParent != null && currentParent != backgroundView) {
+ currentParent.removeView(iconView)
+ currentParent.removeTransientView(iconView)
+ }
+
+ // 3: Add the icon as the starting view.
+ backgroundView.addView(iconView, /* index= */ 0, generateCustomIconLayoutParams(iconView))
+ }
+
private fun View.getCustomIconView(): StatusBarIconView? {
return this.findViewById(CUSTOM_ICON_VIEW_ID)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
index cf07af1..2dce4e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
@@ -21,6 +21,7 @@
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
/** Model representing the display of an ongoing activity as a chip in the status bar. */
sealed class OngoingActivityChipModel {
@@ -132,6 +133,17 @@
"OngoingActivityChipModel.ChipIcon.StatusBarView created even though " +
"Flags.statusBarCallChipNotificationIcon is not enabled"
}
+ StatusBarConnectedDisplays.assertInLegacyMode()
+ }
+ }
+
+ /**
+ * The icon is a custom icon, which is set on a notification, and can be looked up using the
+ * provided [notificationKey]. The icon was likely created by an external app.
+ */
+ data class StatusBarNotificationIcon(val notificationKey: String) : ChipIcon {
+ init {
+ StatusBarConnectedDisplays.assertInNewMode()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt
index c37b01f..a9c2784 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt
@@ -49,7 +49,7 @@
) : ConnectivityState() {
@JvmField var telephonyDisplayInfo = TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false)
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false, false, false)
@JvmField var serviceState: ServiceState? = null
@JvmField var signalStrength: SignalStrength? = null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt
index 614f0f4..d24edda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt
@@ -20,6 +20,7 @@
import android.content.Context
import android.os.Binder
import android.os.RemoteException
+import android.view.Display
import android.view.WindowInsets
import com.android.internal.statusbar.IStatusBarService
import com.android.internal.statusbar.RegisterStatusBarResult
@@ -47,20 +48,32 @@
override fun start() {
StatusBarConnectedDisplays.assertInNewMode()
- val result: RegisterStatusBarResult =
+ val resultPerDisplay: Map<String, RegisterStatusBarResult> =
try {
- barService.registerStatusBar(commandQueue)
+ barService.registerStatusBarForAllDisplays(commandQueue)
} catch (ex: RemoteException) {
ex.rethrowFromSystemServer()
return
}
- createNavigationBar(result)
-
- if ((result.mTransientBarTypes and WindowInsets.Type.statusBars()) != 0) {
- statusBarModeRepository.defaultDisplay.showTransient()
+ resultPerDisplay[Display.DEFAULT_DISPLAY.toString()]?.let {
+ createNavigationBar(it)
+ // Set up the initial icon state
+ val numIcons: Int = it.mIcons.size
+ for (i in 0 until numIcons) {
+ commandQueue.setIcon(it.mIcons.keyAt(i), it.mIcons.valueAt(i))
+ }
}
- val displayId = context.display.displayId
+
+ for ((displayId, result) in resultPerDisplay.entries) {
+ initializeStatusBarForDisplay(displayId.toInt(), result)
+ }
+ }
+
+ private fun initializeStatusBarForDisplay(displayId: Int, result: RegisterStatusBarResult) {
+ if ((result.mTransientBarTypes and WindowInsets.Type.statusBars()) != 0) {
+ statusBarModeRepository.forDisplay(displayId).showTransient()
+ }
val commandQueueCallbacks = commandQueueCallbacksLazy.get()
commandQueueCallbacks.onSystemBarAttributesChanged(
displayId,
@@ -81,12 +94,6 @@
result.mShowImeSwitcher,
)
- // Set up the initial icon state
- val numIcons: Int = result.mIcons.size
- for (i in 0 until numIcons) {
- commandQueue.setIcon(result.mIcons.keyAt(i), result.mIcons.valueAt(i))
- }
-
// set the initial view visibility
val disabledFlags1 = result.mDisabledFlags1
val disabledFlags2 = result.mDisabledFlags2
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
index ef7b1c3..04458f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
@@ -14,10 +14,13 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.statusbar.notification.collection.coordinator
import android.app.Notification
import android.os.UserHandle
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.server.notification.Flags.screenshareNotificationHiding
import com.android.systemui.dagger.qualifiers.Application
@@ -44,9 +47,9 @@
import dagger.Module
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.mapNotNull
-import com.android.app.tracing.coroutines.launchTraced as launch
@Module(includes = [PrivateSensitiveContentCoordinatorModule::class])
interface SensitiveContentCoordinatorModule
@@ -80,6 +83,7 @@
DynamicPrivacyController.Listener,
OnBeforeRenderListListener {
private var inTransitionFromLockedToGone = false
+ private var canSwipeToEnter = false
private val onSensitiveStateChanged = Runnable() { invalidateList("onSensitiveStateChanged") }
@@ -98,7 +102,9 @@
}
override fun attach(pipeline: NotifPipeline) {
- dynamicPrivacyController.addListener(this)
+ if (!SceneContainerFlag.isEnabled) {
+ dynamicPrivacyController.addListener(this)
+ }
if (screenshareNotificationHiding()) {
sensitiveNotificationProtectionController.registerSensitiveStateListener(
onSensitiveStateChanged
@@ -128,6 +134,15 @@
invalidateList("inTransitionFromLockedToGoneChanged")
}
}
+ scope.launch {
+ deviceEntryInteractor.canSwipeToEnter.collect {
+ val canSwipeToEnter = it ?: false
+ if (this@SensitiveContentCoordinatorImpl.canSwipeToEnter != canSwipeToEnter) {
+ this@SensitiveContentCoordinatorImpl.canSwipeToEnter = canSwipeToEnter
+ invalidateList("canSwipeToEnterChanged")
+ }
+ }
+ }
}
}
@@ -168,7 +183,9 @@
(devicePublic &&
!lockscreenUserManager.userAllowsPrivateNotificationsInPublic(currentUserId)) ||
isSensitiveContentProtectionActive
- val dynamicallyUnlocked = dynamicPrivacyController.isDynamicallyUnlocked
+ val dynamicallyUnlocked =
+ if (SceneContainerFlag.isEnabled) canSwipeToEnter
+ else dynamicPrivacyController.isDynamicallyUnlocked
for (entry in extractAllRepresentativeEntries(entries).filter { it.rowExists() }) {
val notifUserId = entry.sbn.user.identifier
val userLockscreen =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
index 98ce163..b56a838 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -162,7 +162,9 @@
val sbIcon = iconBuilder.createIconView(entry)
sbIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
val sbChipIcon: StatusBarIconView?
- if (Flags.statusBarCallChipNotificationIcon()) {
+ if (
+ Flags.statusBarCallChipNotificationIcon() && !StatusBarConnectedDisplays.isEnabled
+ ) {
sbChipIcon = iconBuilder.createIconView(entry)
sbChipIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/EnsureEnrViewsVisibility.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/EnsureEnrViewsVisibility.kt
deleted file mode 100644
index aa63f4ddb..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/EnsureEnrViewsVisibility.kt
+++ /dev/null
@@ -1,61 +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.statusbar.notification.row
-
-import com.android.systemui.Flags
-import com.android.systemui.flags.FlagToken
-import com.android.systemui.flags.RefactorFlagUtils
-
-/** Helper for reading or using the ensure enr views visibility flag state. */
-@Suppress("NOTHING_TO_INLINE")
-object EnsureEnrViewsVisibility {
- /** The aconfig flag name */
- const val FLAG_NAME = Flags.FLAG_ENSURE_ENR_VIEWS_VISIBILITY
-
- /** A token used for dependency declaration */
- val token: FlagToken
- get() = FlagToken(FLAG_NAME, isEnabled)
-
- /** Is the refactor enabled */
- @JvmStatic
- inline val isEnabled
- get() = Flags.ensureEnrViewsVisibility()
-
- /**
- * 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 not enabled to ensure that the refactor author catches issues in testing.
- * Caution!! Using this check incorrectly will cause crashes in nextfood builds!
- */
- @JvmStatic
- inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(isEnabled, FLAG_NAME)
-
- /**
- * Called to ensure code is only run when the flag is disabled. This will throw an exception if
- * the flag is enabled to ensure that the refactor author catches issues in testing.
- */
- @JvmStatic
- inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 7ad65fc..c8811fd 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
@@ -79,7 +79,6 @@
import com.android.internal.widget.CachingIconView;
import com.android.internal.widget.CallLayout;
import com.android.systemui.Flags;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.RefactorFlag;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.PluginListener;
@@ -130,14 +129,12 @@
import com.android.systemui.util.Compile;
import com.android.systemui.util.DumpUtilsKt;
import com.android.systemui.util.ListenerSet;
-import com.android.systemui.wmshell.BubblesManager;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
@@ -151,7 +148,6 @@
NotificationFadeAware.FadeOptimizedNotification {
private static final String TAG = "ExpandableNotifRow";
- private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
private static final boolean DEBUG_ONMEASURE =
Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
private static final int MENU_VIEW_INDEX = 0;
@@ -186,12 +182,10 @@
private LayoutListener mLayoutListener;
private RowContentBindStage mRowContentBindStage;
private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
- private Optional<BubblesManager> mBubblesManagerOptional;
private MetricsLogger mMetricsLogger;
private NotificationChildrenContainerLogger mChildrenContainerLogger;
private ColorUpdateLogger mColorUpdateLogger;
private NotificationDismissibilityProvider mDismissibilityProvider;
- private FeatureFlags mFeatureFlags;
private int mIconTransformContentShift;
private int mMaxHeadsUpHeightBeforeN;
private int mMaxHeadsUpHeightBeforeP;
@@ -342,7 +336,7 @@
*/
private boolean mIgnoreLockscreenConstraints;
- private OnClickListener mExpandClickListener = new OnClickListener() {
+ private final OnClickListener mExpandClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
toggleExpansionState(v, /* shouldLogExpandClickMetric = */true);
@@ -431,13 +425,10 @@
@Nullable
private Runnable mOnIntrinsicHeightReachedRunnable;
- private float mTopRoundnessDuringLaunchAnimation;
- private float mBottomRoundnessDuringLaunchAnimation;
- private float mSmallRoundness;
+ private final float mSmallRoundness;
- private ListenerSet<DismissButtonTargetVisibilityListener>
- mDismissButtonTargetVisibilityListeners
- = new ListenerSet();
+ private final ListenerSet<DismissButtonTargetVisibilityListener>
+ mDismissButtonTargetVisibilityListeners = new ListenerSet<>();
public NotificationContentView[] getLayouts() {
return Arrays.copyOf(mLayouts, mLayouts.length);
@@ -788,7 +779,7 @@
if (targetVisible != null) {
for (DismissButtonTargetVisibilityListener listener :
mDismissButtonTargetVisibilityListeners) {
- listener.onTargetVisibilityChanged(targetVisible.booleanValue());
+ listener.onTargetVisibilityChanged(targetVisible);
}
}
@@ -2024,7 +2015,6 @@
StatusBarStateController statusBarStateController,
PeopleNotificationIdentifier peopleNotificationIdentifier,
OnUserInteractionCallback onUserInteractionCallback,
- Optional<BubblesManager> bubblesManagerOptional,
NotificationGutsManager gutsManager,
NotificationDismissibilityProvider dismissibilityProvider,
MetricsLogger metricsLogger,
@@ -2032,7 +2022,6 @@
ColorUpdateLogger colorUpdateLogger,
SmartReplyConstants smartReplyConstants,
SmartReplyController smartReplyController,
- FeatureFlags featureFlags,
IStatusBarService statusBarService,
UiEventLogger uiEventLogger) {
mEntry = entry;
@@ -2067,13 +2056,11 @@
);
}
mOnUserInteractionCallback = onUserInteractionCallback;
- mBubblesManagerOptional = bubblesManagerOptional;
mNotificationGutsManager = gutsManager;
mMetricsLogger = metricsLogger;
mChildrenContainerLogger = childrenContainerLogger;
mColorUpdateLogger = colorUpdateLogger;
mDismissibilityProvider = dismissibilityProvider;
- mFeatureFlags = featureFlags;
setHapticFeedbackEnabled(!Flags.msdlFeedback());
}
@@ -2470,10 +2457,6 @@
: View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
}
- public CharSequence getActiveRemoteInputText() {
- return mPrivateLayout.getActiveRemoteInputText();
- }
-
/**
* Reset the translation with an animation.
*/
@@ -2548,7 +2531,7 @@
return getTranslationX();
}
- if (mTranslateableViews != null && mTranslateableViews.size() > 0) {
+ if (mTranslateableViews != null && !mTranslateableViews.isEmpty()) {
// All of the views in the list should have same translation, just use first one.
return mTranslateableViews.get(0).getTranslationX();
}
@@ -2603,10 +2586,6 @@
}
private void updateChildrenVisibility() {
- if (EnsureEnrViewsVisibility.isEnabled()) {
- mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
- }
-
boolean hideContentWhileLaunching = mExpandAnimationRunning && mGuts != null
&& mGuts.isExposed();
mPrivateLayout.setVisibility(!mShowingPublic && !mIsSummaryWithChildren
@@ -2687,7 +2666,7 @@
int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, params.getProgress());
if (mNotificationParent != null) {
float parentTranslationY = mNotificationParent.getTranslationY();
- top -= parentTranslationY;
+ top -= (int) parentTranslationY;
mNotificationParent.setTranslationZ(translationZ);
// When the expanding notification is below its parent, the parent must be clipped
@@ -2714,9 +2693,6 @@
float absoluteCenterX = getLocationOnScreen()[0] + getWidth() / 2f - getTranslationX();
setTranslationX(params.getCenterX() - absoluteCenterX);
- final float maxRadius = getMaxRadius();
- mTopRoundnessDuringLaunchAnimation = params.getTopCornerRadius() / maxRadius;
- mBottomRoundnessDuringLaunchAnimation = params.getBottomCornerRadius() / maxRadius;
invalidateOutline();
mBackgroundNormal.setExpandAnimationSize(params.getWidth(), actualHeight);
@@ -2926,7 +2902,6 @@
}
}
-
@Override
public int getHeightWithoutLockscreenConstraints() {
mIgnoreLockscreenConstraints = true;
@@ -3166,13 +3141,7 @@
} else {
mLogger.logSkipResetAllContentAlphas(getEntry());
}
-
- if (!EnsureEnrViewsVisibility.isEnabled()) {
- // mPublicLayout.setVisibility moved to updateChildrenVisibility when the flag is on
- // in order to ensure public and private views are not visible
- // together at the same time.
- mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
- }
+ mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
updateChildrenVisibility();
} else {
animateShowingPublic(delay, duration, mShowingPublic);
@@ -3258,7 +3227,7 @@
notifyHeightChanged(/* needsAnimation= */ false);
}
- public void setChildrenExpanded(boolean expanded, boolean animate) {
+ public void setChildrenExpanded(boolean expanded) {
mChildrenExpanded = expanded;
if (mChildrenContainer != null) {
mChildrenContainer.setChildrenExpanded(expanded);
@@ -3910,10 +3879,6 @@
return mEntry.getSbn().getNotification().isMediaNotification();
}
- public boolean isGroupNotFullyVisible() {
- return getClipTopAmount() > 0 || getTranslationY() < 0;
- }
-
public void setAboveShelf(boolean aboveShelf) {
boolean wasAboveShelf = isAboveShelf();
mAboveShelf = aboveShelf;
@@ -4142,10 +4107,6 @@
mTargetPoint = p;
}
- public Point getTargetPoint() {
- return mTargetPoint;
- }
-
/** Update the minimum roundness based on current state */
private void updateBaseRoundness() {
if (isChildInGroup()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index a150f7f..e06280e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -35,7 +35,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.PluginManager;
@@ -51,6 +51,7 @@
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.collection.render.NodeController;
import com.android.systemui.statusbar.notification.collection.render.NotifViewController;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.dagger.AppName;
import com.android.systemui.statusbar.notification.row.dagger.NotificationKey;
@@ -59,17 +60,14 @@
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationRowStatsLogger;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.policy.SmartReplyConstants;
import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
import com.android.systemui.util.time.SystemClock;
-import com.android.systemui.wmshell.BubblesManager;
import com.google.android.msdl.data.model.MSDLToken;
import com.google.android.msdl.domain.MSDLPlayer;
import java.util.List;
-import java.util.Optional;
import javax.inject.Inject;
import javax.inject.Named;
@@ -108,10 +106,9 @@
private final NotificationGutsManager mNotificationGutsManager;
private final OnUserInteractionCallback mOnUserInteractionCallback;
private final FalsingManager mFalsingManager;
- private final FeatureFlags mFeatureFlags;
+ private final FeatureFlagsClassic mFeatureFlags;
private final boolean mAllowLongPress;
private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
- private final Optional<BubblesManager> mBubblesManagerOptional;
private final SmartReplyConstants mSmartReplyConstants;
private final SmartReplyController mSmartReplyController;
private final ExpandableNotificationRowDragController mDragController;
@@ -271,9 +268,8 @@
@Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
OnUserInteractionCallback onUserInteractionCallback,
FalsingManager falsingManager,
- FeatureFlags featureFlags,
+ FeatureFlagsClassic featureFlags,
PeopleNotificationIdentifier peopleNotificationIdentifier,
- Optional<BubblesManager> bubblesManagerOptional,
NotificationSettingsController settingsController,
ExpandableNotificationRowDragController dragController,
NotificationDismissibilityProvider dismissibilityProvider,
@@ -303,7 +299,6 @@
mAllowLongPress = allowLongPress;
mFeatureFlags = featureFlags;
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
- mBubblesManagerOptional = bubblesManagerOptional;
mSettingsController = settingsController;
mDragController = dragController;
mMetricsLogger = metricsLogger;
@@ -340,7 +335,6 @@
mStatusBarStateController,
mPeopleNotificationIdentifier,
mOnUserInteractionCallback,
- mBubblesManagerOptional,
mNotificationGutsManager,
mDismissibilityProvider,
mMetricsLogger,
@@ -348,7 +342,6 @@
mColorUpdateLogger,
mSmartReplyConstants,
mSmartReplyController,
- mFeatureFlags,
mStatusBarService,
mUiEventLogger
);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index cfca830..99edf65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.stack;
+import static android.app.Flags.notificationsRedesignTemplates;
+
import android.app.Notification;
import android.content.Context;
import android.content.res.Configuration;
@@ -171,7 +173,9 @@
R.dimen.notification_children_container_margin_top);
mNotificationTopPadding = res.getDimensionPixelOffset(
R.dimen.notification_children_container_top_padding);
- mHeaderHeight = mNotificationHeaderMargin + mNotificationTopPadding;
+ mHeaderHeight = notificationsRedesignTemplates()
+ ? res.getDimensionPixelSize(R.dimen.notification_2025_header_height)
+ : mNotificationHeaderMargin + mNotificationTopPadding;
mCollapsedBottomPadding = res.getDimensionPixelOffset(
R.dimen.notification_children_collapsed_bottom_padding);
mEnableShadowOnChildNotifications =
@@ -499,7 +503,7 @@
mGroupHeaderWrapper.setExpanded(mChildrenExpanded);
mGroupHeaderWrapper.onContentUpdated(mContainingNotification);
-
+ resetHeaderVisibilityIfNeeded(mGroupHeader, calculateDesiredHeader());
updateHeaderVisibility(false /* animate */);
updateChildrenAppearance();
@@ -535,6 +539,7 @@
invalidate();
mMinimizedGroupHeaderWrapper.onContentUpdated(mContainingNotification);
+ resetHeaderVisibilityIfNeeded(mMinimizedGroupHeader, calculateDesiredHeader());
updateHeaderVisibility(false /* animate */);
updateChildrenAppearance();
}
@@ -1127,7 +1132,7 @@
final int count = mAttachedChildren.size();
for (int childIdx = 0; childIdx < count; childIdx++) {
ExpandableNotificationRow child = mAttachedChildren.get(childIdx);
- child.setChildrenExpanded(childrenExpanded, false);
+ child.setChildrenExpanded(childrenExpanded);
}
updateHeaderTouchability();
}
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 c7b3fd7..b9e38ab 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
@@ -99,7 +99,6 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.FakeShadowView;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
import com.android.systemui.statusbar.notification.NotificationTransitionAnimatorController;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -110,6 +109,8 @@
import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView;
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -126,7 +127,6 @@
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.util.Assert;
@@ -6860,7 +6860,7 @@
mExpandedGroupView = changedRow;
mNeedsAnimation = true;
}
- changedRow.setChildrenExpanded(expanded, animated);
+ changedRow.setChildrenExpanded(expanded);
onChildHeightChanged(changedRow, false /* needsAnimation */);
runAfterAnimationFinished(new Runnable() {
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 ba707a5..245b1d2 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
@@ -429,9 +429,10 @@
};
/**
- * Recalculate sensitiveness without animation; called when waking up while keyguard occluded.
+ * Recalculate sensitiveness without animation; called when waking up while keyguard occluded,
+ * or whenever we update the Lockscreen public mode.
*/
- public void updateSensitivenessForOccludedWakeup() {
+ public void updateSensitivenessWithoutAnimation() {
updateSensitivenessWithAnimation(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
index 969ff1b..44075af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.stack;
+import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK;
+
import android.annotation.ColorInt;
import android.annotation.Nullable;
import android.annotation.StringRes;
@@ -28,6 +30,8 @@
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.core.view.ViewCompat;
+
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
@@ -69,6 +73,13 @@
mLabelView.setText(mLabelTextId);
}
mLabelView.setAccessibilityHeading(true);
+ ViewCompat.replaceAccessibilityAction(
+ mLabelView,
+ ACTION_CLICK,
+ getResources().getString(
+ R.string.accessibility_notification_section_header_open_settings),
+ null
+ );
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
index d1338ea..f2ef2f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
@@ -313,6 +313,7 @@
// if it is volume panel.
options.setDisallowEnterPictureInPictureWhileLaunching(true)
}
+ intent.collectExtraIntentKeys()
try {
result[0] =
ActivityTaskManager.getService()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 7f95fb0..adfcb71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -53,6 +53,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.qs.flags.QsInCompose;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.res.R;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
@@ -209,10 +210,16 @@
@Override
public void clickTile(ComponentName tile) {
- // Can't inject this because it changes with the QS fragment
- QSPanelController qsPanelController = mCentralSurfaces.getQSPanelController();
- if (qsPanelController != null) {
- qsPanelController.clickTile(tile);
+ if (QsInCompose.isEnabled()) {
+ if (tile != null) {
+ mQSHost.clickTile(tile);
+ }
+ } else {
+ // Can't inject this because it changes with the QS fragment
+ QSPanelController qsPanelController = mCentralSurfaces.getQSPanelController();
+ if (qsPanelController != null) {
+ qsPanelController.clickTile(tile);
+ }
}
}
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 c6af328..7bea480 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -2234,6 +2234,9 @@
// If the state didn't change, we may still need to update public mode
mLockscreenUserManager.updatePublicMode();
+ if (SceneContainerFlag.isEnabled()) {
+ mStackScrollerController.updateSensitivenessWithoutAnimation();
+ }
}
if (mStatusBarStateController.leaveOpenOnKeyguardHide()) {
if (!mStatusBarStateController.isKeyguardRequested()) {
@@ -2681,7 +2684,7 @@
// So if AOD is off or unsupported we need to trigger these updates at screen on
// when the keyguard is occluded.
mLockscreenUserManager.updatePublicMode();
- mStackScrollerController.updateSensitivenessForOccludedWakeup();
+ mStackScrollerController.updateSensitivenessWithoutAnimation();
}
if (mLaunchCameraWhenFinishedWaking) {
startLaunchTransitionTimeout();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
index 1cca3ae..d7cc65d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
@@ -180,6 +180,7 @@
// if it is volume panel.
options.setDisallowEnterPictureInPictureWhileLaunching(true)
}
+ intent.collectExtraIntentKeys()
try {
result[0] =
ActivityTaskManager.getService()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index e4768e8..724ba8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -377,6 +377,7 @@
mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener);
mHomeStatusBarViewBinder.bind(
+ view.getContext().getDisplayId(),
mStatusBar,
mHomeStatusBarViewModel,
/* systemEventChipAnimateIn */ null,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 2166304..c57cede 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -260,6 +260,7 @@
startTimeMs = currentInfo.callStartTime,
notificationIconView = icon,
intent = currentInfo.intent,
+ notificationKey = currentInfo.key,
)
} else {
return OngoingCallModel.NoCall
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
index 1f7bd14..4b71c02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
@@ -90,6 +90,7 @@
startTimeMs = model.whenTime,
notificationIconView = model.statusBarChipIconView,
intent = model.contentIntent,
+ notificationKey = model.key,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
index c2c91b2..1a5dcc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
@@ -46,5 +46,6 @@
val startTimeMs: Long,
val notificationIconView: StatusBarIconView?,
val intent: PendingIntent?,
+ val notificationKey: String,
) : OngoingCallModel
}
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 08a98c3..12f578c 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
@@ -161,6 +161,13 @@
)
.stateIn(scope, SharingStarted.WhileSubscribed(), true)
+ /** True if any known mobile network is currently using a non terrestrial network */
+ val isAnyConnectionNtn =
+ iconsInteractor.icons.aggregateOver(selector = { it.isNonTerrestrial }, false) {
+ nonTerrestrialNetworks ->
+ nonTerrestrialNetworks.any { it == true }
+ }
+
companion object {
const val TAG = "DeviceBasedSatelliteInteractor"
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 f3d5139..ea915ef 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
@@ -114,34 +114,39 @@
private val showIcon =
if (interactor.isOpportunisticSatelliteIconEnabled) {
- canShowIcon
- .flatMapLatest { canShow ->
- if (!canShow) {
- flowOf(false)
- } else {
- combine(
- shouldShowIconForOosAfterHysteresis,
- interactor.connectionState,
- interactor.isWifiActive,
- airplaneModeRepository.isAirplaneMode,
- ) { showForOos, connectionState, isWifiActive, isAirplaneMode ->
- if (isWifiActive || isAirplaneMode) {
- false
- } else {
- showForOos ||
- connectionState == SatelliteConnectionState.On ||
- connectionState == SatelliteConnectionState.Connected
+ canShowIcon
+ .flatMapLatest { canShow ->
+ if (!canShow) {
+ flowOf(false)
+ } else {
+ combine(
+ shouldShowIconForOosAfterHysteresis,
+ interactor.isAnyConnectionNtn,
+ interactor.connectionState,
+ interactor.isWifiActive,
+ airplaneModeRepository.isAirplaneMode,
+ ) { showForOos, anyNtn, connectionState, isWifiActive, isAirplaneMode ->
+ // anyNtn means that there is some mobile network using ntn, and the
+ // mobile icon will show its own satellite icon
+ if (isWifiActive || isAirplaneMode || anyNtn) {
+ false
+ } else {
+ // Show for out of service (which has a hysteresis), or ignore
+ // the hysteresis if we're already connected
+ showForOos ||
+ connectionState == SatelliteConnectionState.On ||
+ connectionState == SatelliteConnectionState.Connected
+ }
}
}
}
- }
- .distinctUntilChanged()
- .logDiffsForTable(
- tableLog,
- columnPrefix = "vm",
- columnName = COL_VISIBLE,
- initialValue = false,
- )
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ tableLog,
+ columnPrefix = "vm",
+ columnName = COL_VISIBLE,
+ initialValue = false,
+ )
} else {
flowOf(false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
index 72df027..d9b2bd1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
@@ -20,9 +20,9 @@
import android.animation.AnimatorListenerAdapter
import android.view.View
import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -31,16 +31,19 @@
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipBinder
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState
import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingIn
import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingOut
import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.RunningChipAnim
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ConnectedDisplaysStatusBarNotificationIconViewStore
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.VisibilityModel
import javax.inject.Inject
+import kotlinx.coroutines.launch
/**
* Interface to assist with binding the [CollapsedStatusBarFragment] to [HomeStatusBarViewModel].
@@ -56,6 +59,7 @@
* to support the chip animations.
*/
fun bind(
+ displayId: Int,
view: View,
viewModel: HomeStatusBarViewModel,
systemEventChipAnimateIn: ((View) -> Unit)?,
@@ -65,8 +69,13 @@
}
@SysUISingleton
-class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinder {
+class HomeStatusBarViewBinderImpl
+@Inject
+constructor(
+ private val viewStoreFactory: ConnectedDisplaysStatusBarNotificationIconViewStore.Factory
+) : HomeStatusBarViewBinder {
override fun bind(
+ displayId: Int,
view: View,
viewModel: HomeStatusBarViewModel,
systemEventChipAnimateIn: ((View) -> Unit)?,
@@ -75,6 +84,14 @@
) {
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
+ val iconViewStore =
+ if (StatusBarConnectedDisplays.isEnabled) {
+ viewStoreFactory.create(displayId).also {
+ lifecycleScope.launch { it.activate() }
+ }
+ } else {
+ null
+ }
launch {
viewModel.isTransitioningFromLockscreenToOccluded.collect {
listener.onStatusBarVisibilityMaybeChanged()
@@ -102,7 +119,11 @@
view.requireViewById(R.id.ongoing_activity_chip_primary)
launch {
viewModel.primaryOngoingActivityChip.collect { primaryChipModel ->
- OngoingActivityChipBinder.bind(primaryChipModel, primaryChipView)
+ OngoingActivityChipBinder.bind(
+ primaryChipModel,
+ primaryChipView,
+ iconViewStore,
+ )
if (StatusBarRootModernization.isEnabled) {
when (primaryChipModel) {
is OngoingActivityChipModel.Shown ->
@@ -142,10 +163,18 @@
view.requireViewById(R.id.ongoing_activity_chip_secondary)
launch {
viewModel.ongoingActivityChips.collect { chips ->
- OngoingActivityChipBinder.bind(chips.primary, primaryChipView)
+ OngoingActivityChipBinder.bind(
+ chips.primary,
+ primaryChipView,
+ iconViewStore,
+ )
// TODO(b/364653005): Don't show the secondary chip if there isn't
// enough space for it.
- OngoingActivityChipBinder.bind(chips.secondary, secondaryChipView)
+ OngoingActivityChipBinder.bind(
+ chips.secondary,
+ secondaryChipView,
+ iconViewStore,
+ )
if (StatusBarRootModernization.isEnabled) {
primaryChipView.adjustVisibility(chips.primary.toVisibilityModel())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
index 812e0eb..5614d82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
@@ -176,6 +176,7 @@
// This binder handles everything else
scope.launch {
statusBarViewBinder.bind(
+ context.displayId,
phoneStatusBarView,
statusBarViewModel,
eventAnimationInteractor::animateStatusBarContentForChipEnter,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
index 9839f9d..12ed647 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
@@ -40,12 +40,16 @@
import java.time.Duration
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.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
/**
* An interactor that performs business logic related to the status and configuration of Zen Mode
@@ -58,6 +62,7 @@
private val zenModeRepository: ZenModeRepository,
private val notificationSettingsRepository: NotificationSettingsRepository,
@Background private val bgDispatcher: CoroutineDispatcher,
+ @Background private val backgroundScope: CoroutineScope,
private val iconLoader: ZenIconLoader,
deviceProvisioningRepository: DeviceProvisioningRepository,
userSetupRepository: UserSetupRepository,
@@ -101,13 +106,16 @@
/**
* Returns the special "manual DND" mode.
*
- * This is only meant as a temporary solution for "legacy" UI pieces that handle DND
- * specifically; any new or migrated features should use modes more generally, through [modes]
- * or [activeModes].
+ * This should only be used when there is a strong reason to handle DND specifically (such as
+ * legacy UI pieces that haven't been updated to use modes more generally, or if the user
+ * explicitly wants a shortcut to DND). Please prefer using [modes] or [activeModes] in all
+ * other scenarios.
*/
- val dndMode: Flow<ZenMode?> by lazy {
+ val dndMode: StateFlow<ZenMode?> by lazy {
ModesUi.assertInNewMode()
- zenModeRepository.modes.map { modes -> modes.singleOrNull { it.isManualDnd } }
+ zenModeRepository.modes
+ .map { modes -> modes.singleOrNull { it.isManualDnd } }
+ .stateIn(scope = backgroundScope, started = SharingStarted.Eagerly, initialValue = null)
}
/** Flow returning the currently active mode(s), if any. */
@@ -201,6 +209,14 @@
zenModeRepository.deactivateMode(zenMode)
}
+ fun deactivateAllModes() {
+ for (mode in zenModeRepository.getModes()) {
+ if (mode.isActive) {
+ deactivateMode(mode)
+ }
+ }
+ }
+
private val zenDuration
get() = notificationSettingsRepository.zenDuration.value
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt
index b83613b..4071918 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt
@@ -20,7 +20,6 @@
import android.media.AudioManager.RINGER_MODE_NORMAL
import android.media.AudioManager.RINGER_MODE_SILENT
import android.media.AudioManager.RINGER_MODE_VIBRATE
-import android.provider.Settings
import com.android.settingslib.volume.data.repository.AudioSystemRepository
import com.android.settingslib.volume.shared.model.RingerMode
import com.android.systemui.plugins.VolumeDialogController
@@ -66,11 +65,6 @@
}
},
currentRingerMode = RingerMode(state.ringerModeInternal),
- isEnabled =
- !(state.zenMode == Settings.Global.ZEN_MODE_ALARMS ||
- state.zenMode == Settings.Global.ZEN_MODE_NO_INTERRUPTIONS ||
- (state.zenMode == Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS &&
- state.disallowRinger)),
isMuted = it.level == 0 || it.muted,
level = it.level,
levelMax = it.levelMax,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/shared/model/VolumeDialogRingerModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/shared/model/VolumeDialogRingerModel.kt
index 3c24e02..84a8280 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/shared/model/VolumeDialogRingerModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/shared/model/VolumeDialogRingerModel.kt
@@ -23,8 +23,6 @@
val availableModes: List<RingerMode>,
/** Current ringer mode internal */
val currentRingerMode: RingerMode,
- /** whether the ringer is allowed given the current ZenMode */
- val isEnabled: Boolean,
/** Whether the current ring stream level is zero or the controller state is muted */
val isMuted: Boolean,
/** Ring stream level */
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
index f98ad45..9eee91b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
@@ -34,6 +34,7 @@
import com.android.systemui.res.R
import com.android.systemui.util.children
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.ringer.ui.util.VolumeDialogRingerDrawerTransitionListener
import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerButtonUiModel
import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerButtonViewModel
import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerDrawerState
@@ -42,6 +43,7 @@
import com.android.systemui.volume.dialog.ringer.ui.viewmodel.VolumeDialogRingerDrawerViewModel
import com.android.systemui.volume.dialog.ui.utils.suspendAnimate
import javax.inject.Inject
+import kotlin.properties.Delegates
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.launchIn
@@ -71,6 +73,27 @@
val drawerContainer = view.requireViewById<MotionLayout>(R.id.volume_ringer_drawer)
val unselectedButtonUiModel = RingerButtonUiModel.getUnselectedButton(view.context)
val selectedButtonUiModel = RingerButtonUiModel.getSelectedButton(view.context)
+ val volumeDialogBgSmallRadius =
+ view.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_background_square_corner_radius
+ )
+ val volumeDialogBgFullRadius =
+ view.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_background_corner_radius
+ )
+ var backgroundAnimationProgress: Float by
+ Delegates.observable(0F) { _, _, progress ->
+ volumeDialogBackgroundView.applyCorners(
+ fullRadius = volumeDialogBgFullRadius,
+ diff = volumeDialogBgFullRadius - volumeDialogBgSmallRadius,
+ progress,
+ )
+ }
+ val ringerDrawerTransitionListener = VolumeDialogRingerDrawerTransitionListener {
+ backgroundAnimationProgress = it
+ }
+ drawerContainer.setTransitionListener(ringerDrawerTransitionListener)
+ volumeDialogBackgroundView.background = volumeDialogBackgroundView.background.mutate()
viewModel.ringerViewModel
.onEach { ringerState ->
when (ringerState) {
@@ -87,10 +110,8 @@
selectedButtonUiModel,
unselectedButtonUiModel,
)
+ ringerDrawerTransitionListener.setProgressChangeEnabled(true)
drawerContainer.closeDrawer(uiModel.currentButtonIndex)
- volumeDialogBackgroundView.setBackgroundResource(
- R.drawable.volume_dialog_background
- )
}
is RingerDrawerState.Closed -> {
@@ -103,11 +124,31 @@
uiModel,
selectedButtonUiModel,
unselectedButtonUiModel,
+ onProgressChanged = { progress, isReverse ->
+ // Let's make button progress when switching matches
+ // motionLayout transition progress. When full radius,
+ // progress is 0.0. When small radius, progress is 1.0.
+ backgroundAnimationProgress =
+ if (isReverse) {
+ 1F - progress
+ } else {
+ progress
+ }
+ },
) {
+ if (
+ uiModel.currentButtonIndex ==
+ uiModel.availableButtons.size - 1
+ ) {
+ ringerDrawerTransitionListener.setProgressChangeEnabled(
+ false
+ )
+ } else {
+ ringerDrawerTransitionListener.setProgressChangeEnabled(
+ true
+ )
+ }
drawerContainer.closeDrawer(uiModel.currentButtonIndex)
- volumeDialogBackgroundView.setBackgroundResource(
- R.drawable.volume_dialog_background
- )
}
}
}
@@ -120,16 +161,18 @@
unselectedButtonUiModel,
)
// Open drawer
+ if (
+ uiModel.currentButtonIndex == uiModel.availableButtons.size - 1
+ ) {
+ ringerDrawerTransitionListener.setProgressChangeEnabled(false)
+ } else {
+ ringerDrawerTransitionListener.setProgressChangeEnabled(true)
+ }
drawerContainer.transitionToState(
R.id.volume_dialog_ringer_drawer_open
)
- if (
- uiModel.currentButtonIndex != uiModel.availableButtons.size - 1
- ) {
- volumeDialogBackgroundView.setBackgroundResource(
- R.drawable.volume_dialog_background_small_radius
- )
- }
+ volumeDialogBackgroundView.background =
+ volumeDialogBackgroundView.background.mutate()
}
}
}
@@ -150,6 +193,7 @@
uiModel: RingerViewModel,
selectedButtonUiModel: RingerButtonUiModel,
unselectedButtonUiModel: RingerButtonUiModel,
+ onProgressChanged: (Float, Boolean) -> Unit = { _, _ -> },
onAnimationEnd: Runnable? = null,
) {
ensureChildCount(R.layout.volume_ringer_button, uiModel.availableButtons.size)
@@ -177,10 +221,26 @@
CLOSE_DRAWER_DELAY,
)
}
-
- // We only need to execute on roundness animation end once.
- selectedButton.animateTo(selectedButtonUiModel, roundnessAnimationEndListener)
- unselectedButton.animateTo(unselectedButtonUiModel)
+ // We only need to execute on roundness animation end and volume dialog background
+ // progress update once because these changes should be applied once on volume dialog
+ // background and ringer drawer views.
+ selectedButton.animateTo(
+ selectedButtonUiModel,
+ if (uiModel.currentButtonIndex == count - 1) {
+ onProgressChanged
+ } else {
+ { _, _ -> }
+ },
+ roundnessAnimationEndListener,
+ )
+ unselectedButton.animateTo(
+ unselectedButtonUiModel,
+ if (previousIndex == count - 1) {
+ onProgressChanged
+ } else {
+ { _, _ -> }
+ },
+ )
} else {
bindButtons(viewModel, uiModel, onAnimationEnd)
}
@@ -196,15 +256,17 @@
uiModel.availableButtons.fastForEachIndexed { index, ringerButton ->
ringerButton?.let {
val view = getChildAt(count - index - 1)
+ val isOpen = uiModel.drawerState is RingerDrawerState.Open
if (index == uiModel.currentButtonIndex) {
view.bindDrawerButton(
- uiModel.selectedButton,
+ if (isOpen) it else uiModel.selectedButton,
viewModel,
+ isOpen,
isSelected = true,
isAnimated = isAnimated,
)
} else {
- view.bindDrawerButton(it, viewModel, isAnimated)
+ view.bindDrawerButton(it, viewModel, isOpen, isAnimated = isAnimated)
}
}
}
@@ -214,12 +276,22 @@
private fun View.bindDrawerButton(
buttonViewModel: RingerButtonViewModel,
viewModel: VolumeDialogRingerDrawerViewModel,
+ isOpen: Boolean,
isSelected: Boolean = false,
isAnimated: Boolean = false,
) {
+ val ringerContentDesc = context.getString(buttonViewModel.contentDescriptionResId)
with(requireViewById<ImageButton>(R.id.volume_drawer_button)) {
setImageResource(buttonViewModel.imageResId)
- contentDescription = context.getString(buttonViewModel.contentDescriptionResId)
+ contentDescription =
+ if (isSelected && !isOpen) {
+ context.getString(
+ R.string.volume_ringer_drawer_closed_content_description,
+ ringerContentDesc,
+ )
+ } else {
+ ringerContentDesc
+ }
if (isSelected && !isAnimated) {
setBackgroundResource(R.drawable.volume_drawer_selection_bg)
setColorFilter(
@@ -354,6 +426,7 @@
private suspend fun ImageButton.animateTo(
ringerButtonUiModel: RingerButtonUiModel,
+ onProgressChanged: (Float, Boolean) -> Unit = { _, _ -> },
roundnessAnimationEndListener: DynamicAnimation.OnAnimationEndListener? = null,
) {
val roundnessAnimation =
@@ -364,6 +437,7 @@
ringerButtonUiModel.cornerRadius - (background as GradientDrawable).cornerRadius
val roundnessAnimationUpdateListener =
DynamicAnimation.OnAnimationUpdateListener { _, value, _ ->
+ onProgressChanged(value, cornerRadiusDiff > 0F)
(background as GradientDrawable).cornerRadius = radius + value * cornerRadiusDiff
background.invalidateSelf()
}
@@ -394,4 +468,9 @@
)
}
}
+
+ private fun View.applyCorners(fullRadius: Int, diff: Int, progress: Float) {
+ (background as GradientDrawable).cornerRadius = fullRadius - progress * diff
+ background.invalidateSelf()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/VolumeDialogRingerDrawerTransitionListener.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/VolumeDialogRingerDrawerTransitionListener.kt
new file mode 100644
index 0000000..6e3db0a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/VolumeDialogRingerDrawerTransitionListener.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.ringer.ui.util
+
+import androidx.constraintlayout.motion.widget.MotionLayout
+
+class VolumeDialogRingerDrawerTransitionListener(private val onProgressChanged: (Float) -> Unit) :
+ MotionLayout.TransitionListener {
+
+ private var notifyProgressChangeEnabled = true
+
+ fun setProgressChangeEnabled(enabled: Boolean) {
+ notifyProgressChangeEnabled = enabled
+ }
+
+ override fun onTransitionStarted(motionLayout: MotionLayout?, startId: Int, endId: Int) {}
+
+ override fun onTransitionChange(
+ motionLayout: MotionLayout?,
+ startId: Int,
+ endId: Int,
+ progress: Float,
+ ) {
+ if (notifyProgressChangeEnabled) {
+ onProgressChanged(progress)
+ }
+ }
+
+ override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {}
+
+ override fun onTransitionTrigger(
+ motionLayout: MotionLayout?,
+ triggerId: Int,
+ positive: Boolean,
+ progress: Float,
+ ) {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
index e646636..627d75e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
@@ -21,10 +21,13 @@
import android.media.AudioManager.RINGER_MODE_NORMAL
import android.media.AudioManager.RINGER_MODE_SILENT
import android.media.AudioManager.RINGER_MODE_VIBRATE
+import android.media.AudioManager.STREAM_RING
import android.os.VibrationEffect
import android.widget.Toast
import com.android.internal.R as internalR
import com.android.settingslib.Utils
+import com.android.settingslib.notification.domain.interactor.NotificationsSoundPolicyInteractor
+import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.RingerMode
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -57,7 +60,8 @@
@Application private val applicationContext: Context,
@VolumeDialog private val coroutineScope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
- private val interactor: VolumeDialogRingerInteractor,
+ soundPolicyInteractor: NotificationsSoundPolicyInteractor,
+ private val ringerInteractor: VolumeDialogRingerInteractor,
private val vibrator: VibratorHelper,
private val volumeDialogLogger: VolumeDialogLogger,
private val visibilityInteractor: VolumeDialogVisibilityInteractor,
@@ -66,10 +70,14 @@
private val drawerState = MutableStateFlow<RingerDrawerState>(RingerDrawerState.Initial)
val ringerViewModel: StateFlow<RingerViewModelState> =
- combine(interactor.ringerModel, drawerState) { ringerModel, state ->
+ combine(
+ soundPolicyInteractor.isZenMuted(AudioStream(STREAM_RING)),
+ ringerInteractor.ringerModel,
+ drawerState,
+ ) { isZenMuted, ringerModel, state ->
level = ringerModel.level
levelMax = ringerModel.levelMax
- ringerModel.toViewModel(state)
+ ringerModel.toViewModel(state, isZenMuted)
}
.flowOn(backgroundDispatcher)
.stateIn(coroutineScope, SharingStarted.Eagerly, RingerViewModelState.Unavailable)
@@ -90,7 +98,7 @@
Events.writeEvent(Events.EVENT_RINGER_TOGGLE, ringerMode.value)
provideTouchFeedback(ringerMode)
maybeShowToast(ringerMode)
- interactor.setRingerMode(ringerMode)
+ ringerInteractor.setRingerMode(ringerMode)
}
visibilityInteractor.resetDismissTimeout()
drawerState.value =
@@ -113,7 +121,7 @@
private fun provideTouchFeedback(ringerMode: RingerMode) {
when (ringerMode.value) {
RINGER_MODE_NORMAL -> {
- interactor.scheduleTouchFeedback()
+ ringerInteractor.scheduleTouchFeedback()
null
}
RINGER_MODE_SILENT -> VibrationEffect.get(VibrationEffect.EFFECT_CLICK)
@@ -123,7 +131,8 @@
}
private fun VolumeDialogRingerModel.toViewModel(
- drawerState: RingerDrawerState
+ drawerState: RingerDrawerState,
+ isZenMuted: Boolean,
): RingerViewModelState {
val currentIndex = availableModes.indexOf(currentRingerMode)
if (currentIndex == -1) {
@@ -132,10 +141,11 @@
return if (currentIndex == -1 || isSingleVolume) {
RingerViewModelState.Unavailable
} else {
- toButtonViewModel(currentRingerMode, isSelectedButton = true)?.let {
+ toButtonViewModel(currentRingerMode, isZenMuted, isSelectedButton = true)?.let {
RingerViewModelState.Available(
RingerViewModel(
- availableButtons = availableModes.map { mode -> toButtonViewModel(mode) },
+ availableButtons =
+ availableModes.map { mode -> toButtonViewModel(mode, isZenMuted) },
currentButtonIndex = currentIndex,
selectedButton = it,
drawerState = drawerState,
@@ -147,6 +157,7 @@
private fun VolumeDialogRingerModel.toButtonViewModel(
ringerMode: RingerMode,
+ isZenMuted: Boolean,
isSelectedButton: Boolean = false,
): RingerButtonViewModel? {
return when (ringerMode.value) {
@@ -176,7 +187,7 @@
)
RINGER_MODE_NORMAL ->
when {
- isMuted && isEnabled ->
+ isMuted && !isZenMuted ->
RingerButtonViewModel(
imageResId =
if (isSelectedButton) {
@@ -226,7 +237,7 @@
private fun maybeShowToast(ringerMode: RingerMode) {
coroutineScope.launch {
- val seenToastCount = interactor.getToastCount()
+ val seenToastCount = ringerInteractor.getToastCount()
if (seenToastCount > SHOW_RINGER_TOAST_COUNT) {
return@launch
}
@@ -260,7 +271,7 @@
)
}
toastText?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_SHORT).show() }
- interactor.updateToastCount(seenToastCount)
+ ringerInteractor.updateToastCount(seenToastCount)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index e52bad9..f305246 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -18,11 +18,12 @@
import android.animation.Animator
import android.animation.ObjectAnimator
+import android.annotation.SuppressLint
import android.view.View
import android.view.animation.DecelerateInterpolator
import com.android.systemui.res.R
-import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
+import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderStateModel
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory
import com.android.systemui.volume.dialog.ui.utils.awaitAnimation
@@ -48,24 +49,27 @@
val sliderView: Slider =
view.requireViewById<Slider>(R.id.volume_dialog_slider).apply {
labelBehavior = LabelFormatter.LABEL_GONE
+ trackIconActiveColor = trackInactiveTintList
}
sliderView.addOnChangeListener { _, value, fromUser ->
viewModel.setStreamVolume(value.roundToInt(), fromUser)
}
- viewModel.model.onEach { it.bindToSlider(sliderView) }.launchIn(this)
+ viewModel.state.onEach { it.bindToSlider(sliderView) }.launchIn(this)
}
- private suspend fun VolumeDialogStreamModel.bindToSlider(slider: Slider) {
+ @SuppressLint("UseCompatLoadingForDrawables")
+ private suspend fun VolumeDialogSliderStateModel.bindToSlider(slider: Slider) {
with(slider) {
- valueFrom = levelMin.toFloat()
- valueTo = levelMax.toFloat()
+ valueFrom = minValue
+ valueTo = maxValue
// coerce the current value to the new value range before animating it
value = value.coerceIn(valueFrom, valueTo)
setValueAnimated(
- level.toFloat(),
+ value,
jankListenerFactory.update(this, PROGRESS_CHANGE_ANIMATION_DURATION_MS),
)
+ trackIconActiveEnd = context.getDrawable(iconRes)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
new file mode 100644
index 0000000..5c39b6f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dialog.sliders.ui.viewmodel
+
+import android.media.AudioManager
+import androidx.annotation.DrawableRes
+import com.android.settingslib.notification.domain.interactor.NotificationsSoundPolicyInteractor
+import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.android.settingslib.volume.shared.model.RingerMode
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOf
+
+class VolumeDialogSliderIconProvider
+@Inject
+constructor(
+ private val notificationsSoundPolicyInteractor: NotificationsSoundPolicyInteractor,
+ private val audioVolumeInteractor: AudioVolumeInteractor,
+) {
+
+ @DrawableRes
+ fun getStreamIcon(
+ stream: Int,
+ level: Int,
+ levelMin: Int,
+ levelMax: Int,
+ isMuted: Boolean,
+ isRoutedToBluetooth: Boolean,
+ ): Flow<Int> {
+ return combine(
+ notificationsSoundPolicyInteractor.isZenMuted(AudioStream(stream)),
+ ringerModeForStream(stream),
+ ) { isZenMuted, ringerMode ->
+ val isStreamOffline = level == 0 || isMuted
+ if (isZenMuted) {
+ // TODO(b/372466264) use icon for the corresponding zenmode
+ return@combine com.android.internal.R.drawable.ic_qs_dnd
+ }
+ when (ringerMode?.value) {
+ AudioManager.RINGER_MODE_VIBRATE ->
+ return@combine R.drawable.ic_volume_ringer_vibrate
+ AudioManager.RINGER_MODE_SILENT -> return@combine R.drawable.ic_ring_volume_off
+ }
+ if (isRoutedToBluetooth) {
+ return@combine if (stream == AudioManager.STREAM_VOICE_CALL) {
+ R.drawable.ic_volume_bt_sco
+ } else {
+ if (isStreamOffline) {
+ R.drawable.ic_volume_media_bt_mute
+ } else {
+ R.drawable.ic_volume_media_bt
+ }
+ }
+ }
+
+ return@combine if (isStreamOffline) {
+ getMutedIconForStream(stream) ?: getIconForStream(stream)
+ } else {
+ if (level < (levelMax + levelMin) / 2) {
+ // This icon is different on TV
+ R.drawable.ic_volume_media_low
+ } else {
+ getIconForStream(stream)
+ }
+ }
+ }
+ }
+
+ @DrawableRes
+ private fun getMutedIconForStream(stream: Int): Int? {
+ return when (stream) {
+ AudioManager.STREAM_MUSIC -> R.drawable.ic_volume_media_mute
+ AudioManager.STREAM_NOTIFICATION -> R.drawable.ic_volume_ringer_mute
+ AudioManager.STREAM_ALARM -> R.drawable.ic_volume_alarm_mute
+ AudioManager.STREAM_SYSTEM -> R.drawable.ic_volume_system_mute
+ else -> null
+ }
+ }
+
+ @DrawableRes
+ private fun getIconForStream(stream: Int): Int {
+ return when (stream) {
+ AudioManager.STREAM_ACCESSIBILITY -> R.drawable.ic_volume_accessibility
+ AudioManager.STREAM_MUSIC -> R.drawable.ic_volume_media
+ AudioManager.STREAM_RING -> R.drawable.ic_ring_volume
+ AudioManager.STREAM_NOTIFICATION -> R.drawable.ic_volume_ringer
+ AudioManager.STREAM_ALARM -> R.drawable.ic_alarm
+ AudioManager.STREAM_VOICE_CALL -> com.android.internal.R.drawable.ic_phone
+ AudioManager.STREAM_SYSTEM -> R.drawable.ic_volume_system
+ else -> error("Unsupported stream: $stream")
+ }
+ }
+
+ /**
+ * Emits [RingerMode] for the [stream] if it's affecting it and null when [RingerMode] doesn't
+ * affect the [stream]
+ */
+ private fun ringerModeForStream(stream: Int): Flow<RingerMode?> {
+ return if (stream == AudioManager.STREAM_RING) {
+ audioVolumeInteractor.ringerMode
+ } else {
+ flowOf(null)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
new file mode 100644
index 0000000..5750c04
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dialog.sliders.ui.viewmodel
+
+import androidx.annotation.DrawableRes
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
+
+data class VolumeDialogSliderStateModel(
+ val minValue: Float,
+ val maxValue: Float,
+ val value: Float,
+ @DrawableRes val iconRes: Int,
+)
+
+fun VolumeDialogStreamModel.toStateModel(@DrawableRes iconRes: Int): VolumeDialogSliderStateModel {
+ return VolumeDialogSliderStateModel(
+ minValue = levelMin.toFloat(),
+ value = level.toFloat(),
+ maxValue = levelMax.toFloat(),
+ iconRes = iconRes,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
index 6dd5b63..2d56524 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
@@ -32,7 +32,9 @@
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
@@ -56,12 +58,12 @@
private val interactor: VolumeDialogSliderInteractor,
private val visibilityInteractor: VolumeDialogVisibilityInteractor,
@VolumeDialog private val coroutineScope: CoroutineScope,
+ private val volumeDialogSliderIconProvider: VolumeDialogSliderIconProvider,
private val systemClock: SystemClock,
) {
private val userVolumeUpdates = MutableStateFlow<VolumeUpdate?>(null)
-
- val model: Flow<VolumeDialogStreamModel> =
+ private val model: Flow<VolumeDialogStreamModel> =
interactor.slider
.filter {
val lastVolumeUpdateTime = userVolumeUpdates.value?.timestampMillis ?: 0
@@ -70,6 +72,21 @@
.stateIn(coroutineScope, SharingStarted.Eagerly, null)
.filterNotNull()
+ val state: Flow<VolumeDialogSliderStateModel> =
+ model.flatMapLatest { streamModel ->
+ with(streamModel) {
+ volumeDialogSliderIconProvider.getStreamIcon(
+ stream = stream,
+ level = level,
+ levelMin = levelMin,
+ levelMax = levelMax,
+ isMuted = muted,
+ isRoutedToBluetooth = routedToBluetooth,
+ )
+ }
+ .map { icon -> streamModel.toStateModel(icon) }
+ }
+
init {
userVolumeUpdates
.filterNotNull()
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 e565de5..02747d7 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,7 +17,9 @@
package com.android.systemui.volume.panel.component.mediaoutput.ui.viewmodel
import android.content.Context
+import android.graphics.Color as GraphicsColor
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.Flags
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Color
import com.android.systemui.common.shared.model.Icon
@@ -77,7 +79,13 @@
ConnectedDeviceViewModel(
label = label,
labelColor =
- Color.Attribute(com.android.internal.R.attr.materialColorOnSurfaceVariant),
+ if (Flags.volumeRedesign()) {
+ Color.Attribute(com.android.internal.R.attr.materialColorOnSurface)
+ } else {
+ Color.Attribute(
+ com.android.internal.R.attr.materialColorOnSurfaceVariant
+ )
+ },
deviceName =
if (mediaOutputModel.isInAudioSharing) {
context.getString(R.string.audio_sharing_description)
@@ -96,11 +104,7 @@
},
)
}
- .stateIn(
- coroutineScope,
- SharingStarted.Eagerly,
- null,
- )
+ .stateIn(coroutineScope, SharingStarted.Eagerly, null)
val deviceIconViewModel: StateFlow<DeviceIconViewModel?> =
mediaOutputComponentInteractor.mediaOutputModel
@@ -121,7 +125,15 @@
icon = icon,
iconColor =
if (mediaOutputModel.canOpenAudioSwitcher) {
- Color.Attribute(com.android.internal.R.attr.materialColorSurface)
+ if (Flags.volumeRedesign()) {
+ Color.Attribute(
+ com.android.internal.R.attr.materialColorOnPrimary
+ )
+ } else {
+ Color.Attribute(
+ com.android.internal.R.attr.materialColorSurface
+ )
+ }
} else {
Color.Attribute(
com.android.internal.R.attr.materialColorSurfaceContainerHighest
@@ -129,7 +141,15 @@
},
backgroundColor =
if (mediaOutputModel.canOpenAudioSwitcher) {
- Color.Attribute(com.android.internal.R.attr.materialColorSecondary)
+ if (Flags.volumeRedesign()) {
+ Color.Attribute(
+ com.android.internal.R.attr.materialColorPrimary
+ )
+ } else {
+ Color.Attribute(
+ com.android.internal.R.attr.materialColorSecondary
+ )
+ }
} else {
Color.Attribute(com.android.internal.R.attr.materialColorOutline)
},
@@ -139,38 +159,29 @@
icon = icon,
iconColor =
if (mediaOutputModel.canOpenAudioSwitcher) {
- Color.Attribute(
- com.android.internal.R.attr.materialColorOnSurfaceVariant
- )
+ if (Flags.volumeRedesign()) {
+ Color.Attribute(
+ com.android.internal.R.attr.materialColorPrimary
+ )
+ } else {
+ Color.Attribute(
+ com.android.internal.R.attr.materialColorOnSurfaceVariant
+ )
+ }
} else {
Color.Attribute(com.android.internal.R.attr.materialColorOutline)
},
- backgroundColor =
- if (mediaOutputModel.canOpenAudioSwitcher) {
- Color.Attribute(com.android.internal.R.attr.materialColorSurface)
- } else {
- Color.Attribute(
- com.android.internal.R.attr.materialColorSurfaceContainerHighest
- )
- },
+ backgroundColor = Color.Loaded(GraphicsColor.TRANSPARENT),
)
}
}
- .stateIn(
- coroutineScope,
- SharingStarted.Eagerly,
- null,
- )
+ .stateIn(coroutineScope, SharingStarted.Eagerly, null)
val enabled: StateFlow<Boolean> =
mediaOutputComponentInteractor.mediaOutputModel
.filterData()
.map { it.canOpenAudioSwitcher }
- .stateIn(
- coroutineScope,
- SharingStarted.Eagerly,
- true,
- )
+ .stateIn(coroutineScope, SharingStarted.Eagerly, true)
fun onBarClick(expandable: Expandable?) {
uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_MEDIA_OUTPUT_CLICKED)
@@ -178,7 +189,7 @@
mediaOutputComponentInteractor.mediaOutputModel.value
actionsInteractor.onBarClick(
(result as? Result.Data<MediaOutputComponentModel>)?.data,
- expandable
+ expandable,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 073781e..0ec71c2 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -48,7 +48,6 @@
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;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -60,6 +59,7 @@
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.dagger.WMComponent;
import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.onehanded.OneHanded;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index ae94544..a41725f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -90,6 +90,7 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.clearInvocations
@RunWith(AndroidJUnit4::class)
@SmallTest
@@ -528,6 +529,8 @@
fun listenForDnd_onDndChange_updatesClockZenMode() =
testScope.runTest {
underTest.listenForDnd(testScope.backgroundScope)
+ runCurrent()
+ clearInvocations(events)
zenModeRepository.activateMode(dndModeId)
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 7c08928..11199cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -44,6 +44,7 @@
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
+import android.content.Context;
import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.media.AudioRecordingConfiguration;
@@ -593,11 +594,11 @@
//untrusted receiver access
mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID,
- TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, true,
+ TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, Context.DEVICE_ID_DEFAULT, true,
AppOpsManager.ATTRIBUTION_FLAG_RECEIVER, TEST_CHAIN_ID);
//untrusted intermediary access
mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID,
- TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, true,
+ TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, Context.DEVICE_ID_DEFAULT, true,
AppOpsManager.ATTRIBUTION_FLAG_INTERMEDIARY, TEST_CHAIN_ID);
assertTrue(mController.getActiveAppOps().isEmpty());
}
@@ -606,11 +607,11 @@
public void testTrustedChainUsagesKept() {
//untrusted accessor access
mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID,
- TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, true,
+ TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, Context.DEVICE_ID_DEFAULT, true,
AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR, TEST_CHAIN_ID);
//trusted access
mController.onOpActiveChanged(AppOpsManager.OPSTR_CAMERA, TEST_UID,
- TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, true,
+ TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, Context.DEVICE_ID_DEFAULT, true,
AppOpsManager.ATTRIBUTION_FLAG_RECEIVER | AppOpsManager.ATTRIBUTION_FLAG_TRUSTED,
TEST_CHAIN_ID);
assertEquals(2, mController.getActiveAppOps().size());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt
new file mode 100644
index 0000000..d7fcb6a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt
@@ -0,0 +1,471 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.education.domain.interactor
+
+import android.content.pm.UserInfo
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.contextualeducation.GestureType.ALL_APPS
+import com.android.systemui.contextualeducation.GestureType.BACK
+import com.android.systemui.contextualeducation.GestureType.HOME
+import com.android.systemui.contextualeducation.GestureType.OVERVIEW
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.education.data.model.GestureEduModel
+import com.android.systemui.education.data.repository.contextualEducationRepository
+import com.android.systemui.education.data.repository.fakeEduClock
+import com.android.systemui.education.shared.model.EducationUiType
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
+import com.android.systemui.inputdevice.tutorial.tutorialSchedulerRepository
+import com.android.systemui.keyboard.data.repository.keyboardRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.testKosmos
+import com.android.systemui.touchpad.data.repository.touchpadRepository
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.hours
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.verify
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+@SmallTest
+@RunWith(ParameterizedAndroidJunit4::class)
+@kotlinx.coroutines.ExperimentalCoroutinesApi
+class KeyboardTouchpadEduInteractorParameterizedTest(private val gestureType: GestureType) :
+ SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val contextualEduInteractor = kosmos.contextualEducationInteractor
+ private val repository = kosmos.contextualEducationRepository
+ private val touchpadRepository = kosmos.touchpadRepository
+ private val keyboardRepository = kosmos.keyboardRepository
+ private val tutorialSchedulerRepository = kosmos.tutorialSchedulerRepository
+ private val userRepository = kosmos.fakeUserRepository
+ private val overviewProxyService = kosmos.mockOverviewProxyService
+
+ private val underTest: KeyboardTouchpadEduInteractor = kosmos.keyboardTouchpadEduInteractor
+ private val eduClock = kosmos.fakeEduClock
+ private val minDurationForNextEdu =
+ KeyboardTouchpadEduInteractor.minIntervalBetweenEdu + 1.seconds
+ private val initialDelayElapsedDuration =
+ KeyboardTouchpadEduInteractor.initialDelayDuration + 1.seconds
+
+ @Before
+ fun setup() {
+ underTest.start()
+ contextualEduInteractor.start()
+ userRepository.setUserInfos(USER_INFOS)
+ testScope.launch {
+ contextualEduInteractor.updateKeyboardFirstConnectionTime()
+ contextualEduInteractor.updateTouchpadFirstConnectionTime()
+ }
+ }
+
+ @Test
+ fun newEducationInfoOnMaxSignalCountReached() =
+ testScope.runTest {
+ triggerMaxEducationSignals(gestureType)
+ val model by collectLastValue(underTest.educationTriggered)
+
+ assertThat(model?.gestureType).isEqualTo(gestureType)
+ }
+
+ @Test
+ fun newEducationToastOn1stEducation() =
+ testScope.runTest {
+ val model by collectLastValue(underTest.educationTriggered)
+ triggerMaxEducationSignals(gestureType)
+
+ assertThat(model?.educationUiType).isEqualTo(EducationUiType.Toast)
+ }
+
+ @Test
+ fun newEducationNotificationOn2ndEducation() =
+ testScope.runTest {
+ val model by collectLastValue(underTest.educationTriggered)
+ triggerMaxEducationSignals(gestureType)
+ // runCurrent() to trigger 1st education
+ runCurrent()
+
+ eduClock.offset(minDurationForNextEdu)
+ triggerMaxEducationSignals(gestureType)
+
+ assertThat(model?.educationUiType).isEqualTo(EducationUiType.Notification)
+ }
+
+ @Test
+ fun noEducationInfoBeforeMaxSignalCountReached() =
+ testScope.runTest {
+ contextualEduInteractor.incrementSignalCount(gestureType)
+ val model by collectLastValue(underTest.educationTriggered)
+ assertThat(model).isNull()
+ }
+
+ @Test
+ fun noEducationInfoWhenShortcutTriggeredPreviously() =
+ testScope.runTest {
+ val model by collectLastValue(underTest.educationTriggered)
+ contextualEduInteractor.updateShortcutTriggerTime(gestureType)
+ triggerMaxEducationSignals(gestureType)
+ assertThat(model).isNull()
+ }
+
+ @Test
+ fun no2ndEducationBeforeMinEduIntervalReached() =
+ testScope.runTest {
+ val models by collectValues(underTest.educationTriggered)
+ triggerMaxEducationSignals(gestureType)
+ runCurrent()
+
+ // Offset a duration that is less than the required education interval
+ eduClock.offset(1.seconds)
+ triggerMaxEducationSignals(gestureType)
+ runCurrent()
+
+ assertThat(models.filterNotNull().size).isEqualTo(1)
+ }
+
+ @Test
+ fun noNewEducationInfoAfterMaxEducationCountReached() =
+ testScope.runTest {
+ val models by collectValues(underTest.educationTriggered)
+ // Trigger 2 educations
+ triggerMaxEducationSignals(gestureType)
+ runCurrent()
+ eduClock.offset(minDurationForNextEdu)
+ triggerMaxEducationSignals(gestureType)
+ runCurrent()
+
+ // Try triggering 3rd education
+ eduClock.offset(minDurationForNextEdu)
+ triggerMaxEducationSignals(gestureType)
+
+ assertThat(models.filterNotNull().size).isEqualTo(2)
+ }
+
+ @Test
+ fun startNewUsageSessionWhen2ndSignalReceivedAfterSessionDeadline() =
+ testScope.runTest {
+ val model by
+ collectLastValue(
+ kosmos.contextualEducationRepository.readGestureEduModelFlow(gestureType)
+ )
+ contextualEduInteractor.incrementSignalCount(gestureType)
+ eduClock.offset(KeyboardTouchpadEduInteractor.usageSessionDuration.plus(1.seconds))
+ val secondSignalReceivedTime = eduClock.instant()
+ contextualEduInteractor.incrementSignalCount(gestureType)
+
+ assertThat(model)
+ .isEqualTo(
+ GestureEduModel(
+ signalCount = 1,
+ usageSessionStartTime = secondSignalReceivedTime,
+ userId = 0,
+ gestureType = gestureType,
+ )
+ )
+ }
+
+ @Test
+ fun newTouchpadConnectionTimeOnFirstTouchpadConnected() =
+ testScope.runTest {
+ setIsAnyTouchpadConnected(true)
+ val model = contextualEduInteractor.getEduDeviceConnectionTime()
+ assertThat(model.touchpadFirstConnectionTime).isEqualTo(eduClock.instant())
+ }
+
+ @Test
+ fun unchangedTouchpadConnectionTimeOnSecondConnection() =
+ testScope.runTest {
+ val firstConnectionTime = eduClock.instant()
+ setIsAnyTouchpadConnected(true)
+ setIsAnyTouchpadConnected(false)
+
+ eduClock.offset(1.hours)
+ setIsAnyTouchpadConnected(true)
+
+ val model = contextualEduInteractor.getEduDeviceConnectionTime()
+ assertThat(model.touchpadFirstConnectionTime).isEqualTo(firstConnectionTime)
+ }
+
+ @Test
+ fun newTouchpadConnectionTimeOnUserChanged() =
+ testScope.runTest {
+ // Touchpad connected for user 0
+ setIsAnyTouchpadConnected(true)
+
+ // Change user
+ eduClock.offset(1.hours)
+ val newUserFirstConnectionTime = eduClock.instant()
+ userRepository.setSelectedUserInfo(USER_INFOS[0])
+ runCurrent()
+
+ val model = contextualEduInteractor.getEduDeviceConnectionTime()
+ assertThat(model.touchpadFirstConnectionTime).isEqualTo(newUserFirstConnectionTime)
+ }
+
+ @Test
+ fun newKeyboardConnectionTimeOnKeyboardConnected() =
+ testScope.runTest {
+ setIsAnyKeyboardConnected(true)
+ val model = contextualEduInteractor.getEduDeviceConnectionTime()
+ assertThat(model.keyboardFirstConnectionTime).isEqualTo(eduClock.instant())
+ }
+
+ @Test
+ fun unchangedKeyboardConnectionTimeOnSecondConnection() =
+ testScope.runTest {
+ val firstConnectionTime = eduClock.instant()
+ setIsAnyKeyboardConnected(true)
+ setIsAnyKeyboardConnected(false)
+
+ eduClock.offset(1.hours)
+ setIsAnyKeyboardConnected(true)
+
+ val model = contextualEduInteractor.getEduDeviceConnectionTime()
+ assertThat(model.keyboardFirstConnectionTime).isEqualTo(firstConnectionTime)
+ }
+
+ @Test
+ fun newKeyboardConnectionTimeOnUserChanged() =
+ testScope.runTest {
+ // Keyboard connected for user 0
+ setIsAnyKeyboardConnected(true)
+
+ // Change user
+ eduClock.offset(1.hours)
+ val newUserFirstConnectionTime = eduClock.instant()
+ userRepository.setSelectedUserInfo(USER_INFOS[0])
+ runCurrent()
+
+ val model = contextualEduInteractor.getEduDeviceConnectionTime()
+ assertThat(model.keyboardFirstConnectionTime).isEqualTo(newUserFirstConnectionTime)
+ }
+
+ @Test
+ fun updateShortcutTimeOnKeyboardShortcutTriggered() =
+ testScope.runTest {
+ // Only All Apps needs to update the keyboard shortcut
+ assumeTrue(gestureType == ALL_APPS)
+ kosmos.contextualEducationRepository.setKeyboardShortcutTriggered(ALL_APPS)
+
+ val model by
+ collectLastValue(
+ kosmos.contextualEducationRepository.readGestureEduModelFlow(ALL_APPS)
+ )
+ assertThat(model?.lastShortcutTriggeredTime).isEqualTo(eduClock.instant())
+ }
+
+ @Test
+ fun dataUpdatedOnIncrementSignalCountWhenTouchpadConnected() =
+ testScope.runTest {
+ assumeTrue(gestureType != ALL_APPS)
+ setUpForInitialDelayElapse()
+ touchpadRepository.setIsAnyTouchpadConnected(true)
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+ val originalValue = model!!.signalCount
+ val listener = getOverviewProxyListener()
+ listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue + 1)
+ }
+
+ @Test
+ fun dataUnchangedOnIncrementSignalCountWhenTouchpadDisconnected() =
+ testScope.runTest {
+ setUpForInitialDelayElapse()
+ touchpadRepository.setIsAnyTouchpadConnected(false)
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+ val originalValue = model!!.signalCount
+ val listener = getOverviewProxyListener()
+ listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue)
+ }
+
+ @Test
+ fun dataUpdatedOnIncrementSignalCountWhenKeyboardConnected() =
+ testScope.runTest {
+ assumeTrue(gestureType == ALL_APPS)
+ setUpForInitialDelayElapse()
+ keyboardRepository.setIsAnyKeyboardConnected(true)
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+ val originalValue = model!!.signalCount
+ val listener = getOverviewProxyListener()
+ listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue + 1)
+ }
+
+ @Test
+ fun dataUnchangedOnIncrementSignalCountWhenKeyboardDisconnected() =
+ testScope.runTest {
+ setUpForInitialDelayElapse()
+ keyboardRepository.setIsAnyKeyboardConnected(false)
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+ val originalValue = model!!.signalCount
+ val listener = getOverviewProxyListener()
+ listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue)
+ }
+
+ @Test
+ fun dataAddedOnUpdateShortcutTriggerTime() =
+ testScope.runTest {
+ val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+ assertThat(model?.lastShortcutTriggeredTime).isNull()
+
+ val listener = getOverviewProxyListener()
+ listener.updateContextualEduStats(/* isTrackpadGesture= */ true, gestureType)
+
+ assertThat(model?.lastShortcutTriggeredTime).isEqualTo(kosmos.fakeEduClock.instant())
+ }
+
+ @Test
+ fun dataUpdatedOnIncrementSignalCountAfterInitialDelay() =
+ testScope.runTest {
+ setUpForDeviceConnection()
+ tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, eduClock.instant())
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+ val originalValue = model!!.signalCount
+ eduClock.offset(initialDelayElapsedDuration)
+ val listener = getOverviewProxyListener()
+ listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue + 1)
+ }
+
+ @Test
+ fun dataUnchangedOnIncrementSignalCountBeforeInitialDelay() =
+ testScope.runTest {
+ setUpForDeviceConnection()
+ tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, eduClock.instant())
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+ val originalValue = model!!.signalCount
+ // No offset to the clock to simulate update before initial delay
+ val listener = getOverviewProxyListener()
+ listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue)
+ }
+
+ @Test
+ fun dataUnchangedOnIncrementSignalCountWithoutOobeLaunchTime() =
+ testScope.runTest {
+ // No update to OOBE launch time to simulate no OOBE is launched yet
+ setUpForDeviceConnection()
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+ val originalValue = model!!.signalCount
+ val listener = getOverviewProxyListener()
+ listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue)
+ }
+
+ private suspend fun setUpForInitialDelayElapse() {
+ tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, eduClock.instant())
+ tutorialSchedulerRepository.updateLaunchTime(DeviceType.KEYBOARD, eduClock.instant())
+ eduClock.offset(initialDelayElapsedDuration)
+ }
+
+ fun logMetricsForToastEducation() =
+ testScope.runTest {
+ triggerMaxEducationSignals(gestureType)
+ runCurrent()
+
+ verify(kosmos.mockEduMetricsLogger)
+ .logContextualEducationTriggered(gestureType, EducationUiType.Toast)
+ }
+
+ @Test
+ fun logMetricsForNotificationEducation() =
+ testScope.runTest {
+ triggerMaxEducationSignals(gestureType)
+ runCurrent()
+
+ eduClock.offset(minDurationForNextEdu)
+ triggerMaxEducationSignals(gestureType)
+ runCurrent()
+
+ verify(kosmos.mockEduMetricsLogger)
+ .logContextualEducationTriggered(gestureType, EducationUiType.Notification)
+ }
+
+ @After
+ fun clear() {
+ testScope.launch { tutorialSchedulerRepository.clear() }
+ }
+
+ private suspend fun triggerMaxEducationSignals(gestureType: GestureType) {
+ // Increment max number of signal to try triggering education
+ for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) {
+ contextualEduInteractor.incrementSignalCount(gestureType)
+ }
+ }
+
+ private fun TestScope.setIsAnyTouchpadConnected(isConnected: Boolean) {
+ touchpadRepository.setIsAnyTouchpadConnected(isConnected)
+ runCurrent()
+ }
+
+ private fun TestScope.setIsAnyKeyboardConnected(isConnected: Boolean) {
+ keyboardRepository.setIsAnyKeyboardConnected(isConnected)
+ runCurrent()
+ }
+
+ private fun setUpForDeviceConnection() {
+ touchpadRepository.setIsAnyTouchpadConnected(true)
+ keyboardRepository.setIsAnyKeyboardConnected(true)
+ }
+
+ private fun getOverviewProxyListener(): OverviewProxyListener {
+ val listenerCaptor = argumentCaptor<OverviewProxyListener>()
+ verify(overviewProxyService).addCallback(listenerCaptor.capture())
+ return listenerCaptor.firstValue
+ }
+
+ companion object {
+ private val USER_INFOS = listOf(UserInfo(101, "Second User", 0))
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getGestureTypes(): List<GestureType> {
+ return listOf(BACK, HOME, OVERVIEW, ALL_APPS)
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
index 2a6d29c..580f631 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2024 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,19 +16,17 @@
package com.android.systemui.education.domain.interactor
-import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.contextualeducation.GestureType
-import com.android.systemui.contextualeducation.GestureType.ALL_APPS
import com.android.systemui.contextualeducation.GestureType.BACK
import com.android.systemui.contextualeducation.GestureType.HOME
import com.android.systemui.contextualeducation.GestureType.OVERVIEW
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.education.data.model.GestureEduModel
-import com.android.systemui.education.data.repository.contextualEducationRepository
import com.android.systemui.education.data.repository.fakeEduClock
+import com.android.systemui.education.shared.model.EducationInfo
import com.android.systemui.education.shared.model.EducationUiType
import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
import com.android.systemui.inputdevice.tutorial.tutorialSchedulerRepository
@@ -37,50 +35,42 @@
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
import com.android.systemui.testKosmos
import com.android.systemui.touchpad.data.repository.touchpadRepository
-import com.android.systemui.user.data.repository.fakeUserRepository
import com.google.common.truth.Truth.assertThat
-import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.After
-import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.verify
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4
-import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(ParameterizedAndroidJunit4::class)
+@RunWith(AndroidJUnit4::class)
@kotlinx.coroutines.ExperimentalCoroutinesApi
-class KeyboardTouchpadEduInteractorTest(private val gestureType: GestureType) : SysuiTestCase() {
+class KeyboardTouchpadEduInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val contextualEduInteractor = kosmos.contextualEducationInteractor
- private val repository = kosmos.contextualEducationRepository
private val touchpadRepository = kosmos.touchpadRepository
private val keyboardRepository = kosmos.keyboardRepository
private val tutorialSchedulerRepository = kosmos.tutorialSchedulerRepository
- private val userRepository = kosmos.fakeUserRepository
private val overviewProxyService = kosmos.mockOverviewProxyService
private val underTest: KeyboardTouchpadEduInteractor = kosmos.keyboardTouchpadEduInteractor
private val eduClock = kosmos.fakeEduClock
- private val minDurationForNextEdu =
- KeyboardTouchpadEduInteractor.minIntervalBetweenEdu + 1.seconds
private val initialDelayElapsedDuration =
KeyboardTouchpadEduInteractor.initialDelayDuration + 1.seconds
+ private val minIntervalForEduNotification =
+ KeyboardTouchpadEduInteractor.minIntervalBetweenEdu + 1.seconds
@Before
fun setup() {
underTest.start()
contextualEduInteractor.start()
- userRepository.setUserInfos(USER_INFOS)
testScope.launch {
contextualEduInteractor.updateKeyboardFirstConnectionTime()
contextualEduInteractor.updateTouchpadFirstConnectionTime()
@@ -88,312 +78,76 @@
}
@Test
- fun newEducationInfoOnMaxSignalCountReached() =
- testScope.runTest {
- triggerMaxEducationSignals(gestureType)
- val model by collectLastValue(underTest.educationTriggered)
-
- assertThat(model?.gestureType).isEqualTo(gestureType)
- }
-
- @Test
- fun newEducationToastOn1stEducation() =
- testScope.runTest {
- val model by collectLastValue(underTest.educationTriggered)
- triggerMaxEducationSignals(gestureType)
-
- assertThat(model?.educationUiType).isEqualTo(EducationUiType.Toast)
- }
-
- @Test
- fun newEducationNotificationOn2ndEducation() =
- testScope.runTest {
- val model by collectLastValue(underTest.educationTriggered)
- triggerMaxEducationSignals(gestureType)
- // runCurrent() to trigger 1st education
- runCurrent()
-
- eduClock.offset(minDurationForNextEdu)
- triggerMaxEducationSignals(gestureType)
-
- assertThat(model?.educationUiType).isEqualTo(EducationUiType.Notification)
- }
-
- @Test
- fun noEducationInfoBeforeMaxSignalCountReached() =
- testScope.runTest {
- contextualEduInteractor.incrementSignalCount(gestureType)
- val model by collectLastValue(underTest.educationTriggered)
- assertThat(model).isNull()
- }
-
- @Test
- fun noEducationInfoWhenShortcutTriggeredPreviously() =
- testScope.runTest {
- val model by collectLastValue(underTest.educationTriggered)
- contextualEduInteractor.updateShortcutTriggerTime(gestureType)
- triggerMaxEducationSignals(gestureType)
- assertThat(model).isNull()
- }
-
- @Test
- fun no2ndEducationBeforeMinEduIntervalReached() =
- testScope.runTest {
- val models by collectValues(underTest.educationTriggered)
- triggerMaxEducationSignals(gestureType)
- runCurrent()
-
- // Offset a duration that is less than the required education interval
- eduClock.offset(1.seconds)
- triggerMaxEducationSignals(gestureType)
- runCurrent()
-
- assertThat(models.filterNotNull().size).isEqualTo(1)
- }
-
- @Test
- fun noNewEducationInfoAfterMaxEducationCountReached() =
- testScope.runTest {
- val models by collectValues(underTest.educationTriggered)
- // Trigger 2 educations
- triggerMaxEducationSignals(gestureType)
- runCurrent()
- eduClock.offset(minDurationForNextEdu)
- triggerMaxEducationSignals(gestureType)
- runCurrent()
-
- // Try triggering 3rd education
- eduClock.offset(minDurationForNextEdu)
- triggerMaxEducationSignals(gestureType)
-
- assertThat(models.filterNotNull().size).isEqualTo(2)
- }
-
- @Test
- fun startNewUsageSessionWhen2ndSignalReceivedAfterSessionDeadline() =
- testScope.runTest {
- val model by
- collectLastValue(
- kosmos.contextualEducationRepository.readGestureEduModelFlow(gestureType)
- )
- contextualEduInteractor.incrementSignalCount(gestureType)
- eduClock.offset(KeyboardTouchpadEduInteractor.usageSessionDuration.plus(1.seconds))
- val secondSignalReceivedTime = eduClock.instant()
- contextualEduInteractor.incrementSignalCount(gestureType)
-
- assertThat(model)
- .isEqualTo(
- GestureEduModel(
- signalCount = 1,
- usageSessionStartTime = secondSignalReceivedTime,
- userId = 0,
- gestureType = gestureType,
- )
- )
- }
-
- @Test
- fun newTouchpadConnectionTimeOnFirstTouchpadConnected() =
- testScope.runTest {
- setIsAnyTouchpadConnected(true)
- val model = contextualEduInteractor.getEduDeviceConnectionTime()
- assertThat(model.touchpadFirstConnectionTime).isEqualTo(eduClock.instant())
- }
-
- @Test
- fun unchangedTouchpadConnectionTimeOnSecondConnection() =
- testScope.runTest {
- val firstConnectionTime = eduClock.instant()
- setIsAnyTouchpadConnected(true)
- setIsAnyTouchpadConnected(false)
-
- eduClock.offset(1.hours)
- setIsAnyTouchpadConnected(true)
-
- val model = contextualEduInteractor.getEduDeviceConnectionTime()
- assertThat(model.touchpadFirstConnectionTime).isEqualTo(firstConnectionTime)
- }
-
- @Test
- fun newTouchpadConnectionTimeOnUserChanged() =
- testScope.runTest {
- // Touchpad connected for user 0
- setIsAnyTouchpadConnected(true)
-
- // Change user
- eduClock.offset(1.hours)
- val newUserFirstConnectionTime = eduClock.instant()
- userRepository.setSelectedUserInfo(USER_INFOS[0])
- runCurrent()
-
- val model = contextualEduInteractor.getEduDeviceConnectionTime()
- assertThat(model.touchpadFirstConnectionTime).isEqualTo(newUserFirstConnectionTime)
- }
-
- @Test
- fun newKeyboardConnectionTimeOnKeyboardConnected() =
- testScope.runTest {
- setIsAnyKeyboardConnected(true)
- val model = contextualEduInteractor.getEduDeviceConnectionTime()
- assertThat(model.keyboardFirstConnectionTime).isEqualTo(eduClock.instant())
- }
-
- @Test
- fun unchangedKeyboardConnectionTimeOnSecondConnection() =
- testScope.runTest {
- val firstConnectionTime = eduClock.instant()
- setIsAnyKeyboardConnected(true)
- setIsAnyKeyboardConnected(false)
-
- eduClock.offset(1.hours)
- setIsAnyKeyboardConnected(true)
-
- val model = contextualEduInteractor.getEduDeviceConnectionTime()
- assertThat(model.keyboardFirstConnectionTime).isEqualTo(firstConnectionTime)
- }
-
- @Test
- fun newKeyboardConnectionTimeOnUserChanged() =
- testScope.runTest {
- // Keyboard connected for user 0
- setIsAnyKeyboardConnected(true)
-
- // Change user
- eduClock.offset(1.hours)
- val newUserFirstConnectionTime = eduClock.instant()
- userRepository.setSelectedUserInfo(USER_INFOS[0])
- runCurrent()
-
- val model = contextualEduInteractor.getEduDeviceConnectionTime()
- assertThat(model.keyboardFirstConnectionTime).isEqualTo(newUserFirstConnectionTime)
- }
-
- @Test
- fun updateShortcutTimeOnKeyboardShortcutTriggered() =
- testScope.runTest {
- // Only All Apps needs to update the keyboard shortcut
- assumeTrue(gestureType == ALL_APPS)
- kosmos.contextualEducationRepository.setKeyboardShortcutTriggered(ALL_APPS)
-
- val model by
- collectLastValue(
- kosmos.contextualEducationRepository.readGestureEduModelFlow(ALL_APPS)
- )
- assertThat(model?.lastShortcutTriggeredTime).isEqualTo(eduClock.instant())
- }
-
- @Test
- fun dataUpdatedOnIncrementSignalCountWhenTouchpadConnected() =
- testScope.runTest {
- assumeTrue(gestureType != ALL_APPS)
- setUpForInitialDelayElapse()
- touchpadRepository.setIsAnyTouchpadConnected(true)
-
- val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
- val originalValue = model!!.signalCount
- val listener = getOverviewProxyListener()
- listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
-
- assertThat(model?.signalCount).isEqualTo(originalValue + 1)
- }
-
- @Test
- fun dataUnchangedOnIncrementSignalCountWhenTouchpadDisconnected() =
- testScope.runTest {
- setUpForInitialDelayElapse()
- touchpadRepository.setIsAnyTouchpadConnected(false)
-
- val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
- val originalValue = model!!.signalCount
- val listener = getOverviewProxyListener()
- listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
-
- assertThat(model?.signalCount).isEqualTo(originalValue)
- }
-
- @Test
- fun dataUpdatedOnIncrementSignalCountWhenKeyboardConnected() =
- testScope.runTest {
- assumeTrue(gestureType == ALL_APPS)
- setUpForInitialDelayElapse()
- keyboardRepository.setIsAnyKeyboardConnected(true)
-
- val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
- val originalValue = model!!.signalCount
- val listener = getOverviewProxyListener()
- listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
-
- assertThat(model?.signalCount).isEqualTo(originalValue + 1)
- }
-
- @Test
- fun dataUnchangedOnIncrementSignalCountWhenKeyboardDisconnected() =
- testScope.runTest {
- setUpForInitialDelayElapse()
- keyboardRepository.setIsAnyKeyboardConnected(false)
-
- val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
- val originalValue = model!!.signalCount
- val listener = getOverviewProxyListener()
- listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
-
- assertThat(model?.signalCount).isEqualTo(originalValue)
- }
-
- @Test
- fun dataAddedOnUpdateShortcutTriggerTime() =
- testScope.runTest {
- val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
- assertThat(model?.lastShortcutTriggeredTime).isNull()
-
- val listener = getOverviewProxyListener()
- listener.updateContextualEduStats(/* isTrackpadGesture= */ true, gestureType)
-
- assertThat(model?.lastShortcutTriggeredTime).isEqualTo(kosmos.fakeEduClock.instant())
- }
-
- @Test
- fun dataUpdatedOnIncrementSignalCountAfterInitialDelay() =
+ fun newEducationToastBeforeMaxToastsPerSessionTriggered() =
testScope.runTest {
setUpForDeviceConnection()
- tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, eduClock.instant())
+ setUpForInitialDelayElapse()
+ val model by collectLastValue(underTest.educationTriggered)
- val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
- val originalValue = model!!.signalCount
- eduClock.offset(initialDelayElapsedDuration)
- val listener = getOverviewProxyListener()
- listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+ triggerEducation(HOME)
- assertThat(model?.signalCount).isEqualTo(originalValue + 1)
+ assertThat(model).isEqualTo(EducationInfo(HOME, EducationUiType.Toast, userId = 0))
}
@Test
- fun dataUnchangedOnIncrementSignalCountBeforeInitialDelay() =
+ fun noEducationToastAfterMaxToastsPerSessionTriggered() =
testScope.runTest {
setUpForDeviceConnection()
- tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, eduClock.instant())
+ setUpForInitialDelayElapse()
+ val models by collectValues(underTest.educationTriggered.filterNotNull())
+ // Show two toasts of other gestures
+ triggerEducation(HOME)
+ triggerEducation(BACK)
- val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
- val originalValue = model!!.signalCount
- // No offset to the clock to simulate update before initial delay
- val listener = getOverviewProxyListener()
- listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+ triggerEducation(OVERVIEW)
- assertThat(model?.signalCount).isEqualTo(originalValue)
+ // No new toast education besides the 2 triggered at first
+ val firstEdu = EducationInfo(HOME, EducationUiType.Toast, userId = 0)
+ val secondEdu = EducationInfo(BACK, EducationUiType.Toast, userId = 0)
+ assertThat(models).containsExactly(firstEdu, secondEdu).inOrder()
}
@Test
- fun dataUnchangedOnIncrementSignalCountWithoutOobeLaunchTime() =
+ fun newEducationToastAfterMinIntervalElapsedWhenMaxToastsPerSessionTriggered() =
testScope.runTest {
- // No update to OOBE launch time to simulate no OOBE is launched yet
setUpForDeviceConnection()
+ setUpForInitialDelayElapse()
+ val models by collectValues(underTest.educationTriggered.filterNotNull())
+ // Show two toasts of other gestures
+ triggerEducation(HOME)
+ triggerEducation(BACK)
- val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
- val originalValue = model!!.signalCount
- val listener = getOverviewProxyListener()
- listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+ // Trigger toast after an usage session has elapsed
+ eduClock.offset(KeyboardTouchpadEduInteractor.usageSessionDuration + 1.seconds)
+ triggerEducation(OVERVIEW)
- assertThat(model?.signalCount).isEqualTo(originalValue)
+ val firstEdu = EducationInfo(HOME, EducationUiType.Toast, userId = 0)
+ val secondEdu = EducationInfo(BACK, EducationUiType.Toast, userId = 0)
+ val thirdEdu = EducationInfo(OVERVIEW, EducationUiType.Toast, userId = 0)
+ assertThat(models).containsExactly(firstEdu, secondEdu, thirdEdu).inOrder()
+ }
+
+ @Test
+ fun newEducationNotificationAfterMaxToastsPerSessionTriggered() =
+ testScope.runTest {
+ setUpForDeviceConnection()
+ setUpForInitialDelayElapse()
+ val models by collectValues(underTest.educationTriggered.filterNotNull())
+ triggerEducation(BACK)
+
+ // Offset to let min interval for notification elapse so we could show edu notification
+ // for BACK. It would be a new usage session too because the interval (7 days) is
+ // longer than a usage session (3 days)
+ eduClock.offset(minIntervalForEduNotification)
+ triggerEducation(HOME)
+ triggerEducation(OVERVIEW)
+ triggerEducation(BACK)
+
+ val firstEdu = EducationInfo(BACK, EducationUiType.Toast, userId = 0)
+ val secondEdu = EducationInfo(HOME, EducationUiType.Toast, userId = 0)
+ val thirdEdu = EducationInfo(OVERVIEW, EducationUiType.Toast, userId = 0)
+ val fourthEdu = EducationInfo(BACK, EducationUiType.Notification, userId = 0)
+ assertThat(models).containsExactly(firstEdu, secondEdu, thirdEdu, fourthEdu).inOrder()
}
private suspend fun setUpForInitialDelayElapse() {
@@ -402,51 +156,6 @@
eduClock.offset(initialDelayElapsedDuration)
}
- fun logMetricsForToastEducation() =
- testScope.runTest {
- triggerMaxEducationSignals(gestureType)
- runCurrent()
-
- verify(kosmos.mockEduMetricsLogger)
- .logContextualEducationTriggered(gestureType, EducationUiType.Toast)
- }
-
- @Test
- fun logMetricsForNotificationEducation() =
- testScope.runTest {
- triggerMaxEducationSignals(gestureType)
- runCurrent()
-
- eduClock.offset(minDurationForNextEdu)
- triggerMaxEducationSignals(gestureType)
- runCurrent()
-
- verify(kosmos.mockEduMetricsLogger)
- .logContextualEducationTriggered(gestureType, EducationUiType.Notification)
- }
-
- @After
- fun clear() {
- testScope.launch { tutorialSchedulerRepository.clear() }
- }
-
- private suspend fun triggerMaxEducationSignals(gestureType: GestureType) {
- // Increment max number of signal to try triggering education
- for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) {
- contextualEduInteractor.incrementSignalCount(gestureType)
- }
- }
-
- private fun TestScope.setIsAnyTouchpadConnected(isConnected: Boolean) {
- touchpadRepository.setIsAnyTouchpadConnected(isConnected)
- runCurrent()
- }
-
- private fun TestScope.setIsAnyKeyboardConnected(isConnected: Boolean) {
- keyboardRepository.setIsAnyKeyboardConnected(isConnected)
- runCurrent()
- }
-
private fun setUpForDeviceConnection() {
touchpadRepository.setIsAnyTouchpadConnected(true)
keyboardRepository.setIsAnyKeyboardConnected(true)
@@ -458,13 +167,12 @@
return listenerCaptor.firstValue
}
- companion object {
- private val USER_INFOS = listOf(UserInfo(101, "Second User", 0))
-
- @JvmStatic
- @Parameters(name = "{0}")
- fun getGestureTypes(): List<GestureType> {
- return listOf(BACK, HOME, OVERVIEW, ALL_APPS)
+ private fun TestScope.triggerEducation(gestureType: GestureType) {
+ // Increment max number of signal to try triggering education
+ for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) {
+ val listener = getOverviewProxyListener()
+ listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
}
+ runCurrent()
}
}
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 194f456..38acd23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -137,6 +137,7 @@
import com.android.systemui.util.settings.SystemSettings;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.wallpapers.data.repository.FakeWallpaperRepository;
+import com.android.window.flags.Flags;
import com.android.wm.shell.keyguard.KeyguardTransitions;
import kotlinx.coroutines.CoroutineDispatcher;
@@ -157,6 +158,10 @@
@TestableLooper.RunWithLooper
@SmallTest
public class KeyguardViewMediatorTest extends SysuiTestCase {
+
+ private static final boolean ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS =
+ Flags.ensureKeyguardDoesTransitionStarting();
+
private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
private KeyguardViewMediator mViewMediator;
@@ -1164,6 +1169,29 @@
*/
private void assertATMSLockScreenShowing(boolean showing)
throws RemoteException {
+
+ if (ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS) {
+ // ATMS is called via bgExecutor, so make sure to run all of those calls first.
+ processAllMessagesAndBgExecutorMessages();
+
+ final InOrder orderedSetLockScreenShownCalls = inOrder(mKeyguardTransitions);
+ final ArgumentCaptor<Boolean> showingCaptor = ArgumentCaptor.forClass(Boolean.class);
+ orderedSetLockScreenShownCalls
+ .verify(mKeyguardTransitions, atLeastOnce())
+ .startKeyguardTransition(showingCaptor.capture(), anyBoolean());
+
+ // The captor will have the most recent startKeyguardTransition call's value.
+ assertEquals(showing, showingCaptor.getValue());
+
+ // We're now just after the last startKeyguardTransition call. If we expect the
+ // lockscreen to be showing, ensure that we didn't subsequently ask for it to go away.
+ if (showing) {
+ orderedSetLockScreenShownCalls.verify(mKeyguardTransitions, never())
+ .startKeyguardTransition(eq(false), anyBoolean());
+ }
+ return;
+ }
+
// ATMS is called via bgExecutor, so make sure to run all of those calls first.
processAllMessagesAndBgExecutorMessages();
@@ -1192,6 +1220,20 @@
// ATMS is called via bgExecutor, so make sure to run all of those calls first.
processAllMessagesAndBgExecutorMessages();
+ if (ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS) {
+ final InOrder orderedGoingAwayCalls = inOrder(mKeyguardTransitions);
+ orderedGoingAwayCalls.verify(mKeyguardTransitions, atLeastOnce())
+ .startKeyguardTransition(eq(false) /* keyguardShowing */,
+ eq(false) /* aodShowing */);
+
+ // Advance the inOrder to just past the last goingAway call. Let's make sure we didn't
+ // re-show the lockscreen, which would cancel going away.
+ orderedGoingAwayCalls.verify(mKeyguardTransitions, never())
+ .startKeyguardTransition(eq(true) /* keyguardShowing */,
+ anyBoolean() /* aodShowing */);
+ return;
+ }
+
final InOrder orderedGoingAwayCalls = inOrder(mActivityTaskManagerService);
orderedGoingAwayCalls.verify(mActivityTaskManagerService, atLeastOnce())
.keyguardGoingAway(anyInt());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
index d88d69d..d2317e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
@@ -22,7 +22,10 @@
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.filter
import androidx.compose.ui.test.hasContentDescription
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule
@@ -182,11 +185,14 @@
}
private fun ComposeContentTestRule.assertTileGridContainsExactly(specs: List<String>) {
- onNodeWithTag(CURRENT_TILES_GRID_TEST_TAG).onChildren().apply {
- fetchSemanticsNodes().forEachIndexed { index, _ ->
- get(index).assert(hasContentDescription(specs[index]))
+ onNodeWithTag(CURRENT_TILES_GRID_TEST_TAG)
+ .onChildren()
+ .filter(SemanticsMatcher.keyIsDefined(SemanticsProperties.ContentDescription))
+ .apply {
+ fetchSemanticsNodes().forEachIndexed { index, _ ->
+ get(index).assert(hasContentDescription(specs[index]))
+ }
}
- }
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
index 9a924ed..d8d6f2e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
@@ -4,9 +4,10 @@
import android.os.Handler
import android.os.Looper
import android.os.UserManager
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.telephony.flags.Flags
@@ -22,8 +23,11 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.BluetoothController
import com.android.systemui.util.mockito.any
@@ -41,11 +45,17 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
-class BluetoothTileTest : SysuiTestCase() {
+class BluetoothTileTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
@Mock private lateinit var qsLogger: QSLogger
@Mock private lateinit var qsHost: QSHost
@@ -81,7 +91,7 @@
qsLogger,
bluetoothController,
featureFlags,
- bluetoothTileDialogViewModel
+ bluetoothTileDialogViewModel,
)
tile.initialize()
@@ -109,8 +119,7 @@
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_off))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_bluetooth_icon_off))
}
@Test
@@ -121,8 +130,7 @@
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_off))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_bluetooth_icon_off))
}
@Test
@@ -133,8 +141,7 @@
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_on))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_bluetooth_icon_on))
}
@Test
@@ -145,8 +152,7 @@
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_search))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_bluetooth_icon_search))
}
@Test
@@ -161,11 +167,10 @@
.isEqualTo(
mContext.getString(
R.string.quick_settings_bluetooth_secondary_label_battery_level,
- Utils.formatPercentage(50)
+ Utils.formatPercentage(50),
)
)
- verify(bluetoothController)
- .addOnMetadataChangedListener(eq(cachedDevice), any(), any())
+ verify(bluetoothController).addOnMetadataChangedListener(eq(cachedDevice), any(), any())
}
@Test
@@ -186,7 +191,7 @@
.isEqualTo(
mContext.getString(
R.string.quick_settings_bluetooth_secondary_label_battery_level,
- Utils.formatPercentage(25)
+ Utils.formatPercentage(25),
)
)
verify(bluetoothController, times(1))
@@ -197,7 +202,7 @@
fun handleClick_hasSatelliteFeatureButNoQsTileDialogAndClickIsProcessing_doNothing() {
mSetFlagsRule.enableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
`when`(featureFlags.isEnabled(com.android.systemui.flags.Flags.BLUETOOTH_QS_TILE_DIALOG))
- .thenReturn(false)
+ .thenReturn(false)
`when`(clickJob.isCompleted).thenReturn(false)
tile.mClickJob = clickJob
@@ -210,7 +215,7 @@
fun handleClick_noSatelliteFeatureAndNoQsTileDialog_directSetBtEnable() {
mSetFlagsRule.disableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
`when`(featureFlags.isEnabled(com.android.systemui.flags.Flags.BLUETOOTH_QS_TILE_DIALOG))
- .thenReturn(false)
+ .thenReturn(false)
tile.handleClick(null)
@@ -221,7 +226,7 @@
fun handleClick_noSatelliteFeatureButHasQsTileDialog_showDialog() {
mSetFlagsRule.disableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
`when`(featureFlags.isEnabled(com.android.systemui.flags.Flags.BLUETOOTH_QS_TILE_DIALOG))
- .thenReturn(true)
+ .thenReturn(true)
tile.handleClick(null)
@@ -265,7 +270,7 @@
qsLogger: QSLogger,
bluetoothController: BluetoothController,
featureFlags: FeatureFlagsClassic,
- bluetoothTileDialogViewModel: BluetoothTileDialogViewModel
+ bluetoothTileDialogViewModel: BluetoothTileDialogViewModel,
) :
BluetoothTile(
qsHost,
@@ -279,13 +284,13 @@
qsLogger,
bluetoothController,
featureFlags,
- bluetoothTileDialogViewModel
+ bluetoothTileDialogViewModel,
) {
var restrictionChecked: String? = null
override fun checkIfRestrictionEnforcedByAdminOnly(
state: QSTile.State?,
- userRestriction: String?
+ userRestriction: String?,
) {
restrictionChecked = userRestriction
}
@@ -321,7 +326,7 @@
fun listenToDeviceMetadata(
state: QSTile.BooleanState,
cachedDevice: CachedBluetoothDevice,
- batteryLevel: Int
+ batteryLevel: Int,
) {
val btDevice = mock<BluetoothDevice>()
whenever(cachedDevice.device).thenReturn(btDevice)
@@ -332,4 +337,20 @@
addConnectedDevice(cachedDevice)
tile.handleUpdateState(state, /* arg= */ null)
}
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
index 6a43a61..55fb6da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
@@ -21,15 +21,15 @@
import android.content.SharedPreferences
import android.os.Handler
import android.platform.test.annotations.DisableFlags
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.provider.Settings
import android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
import android.provider.Settings.Global.ZEN_MODE_OFF
import android.testing.TestableLooper
import android.view.ContextThemeWrapper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
@@ -39,14 +39,19 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ZenModeController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
+import java.io.File
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -55,56 +60,55 @@
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-import java.io.File
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@DisableFlags(android.app.Flags.FLAG_MODES_UI)
-class DndTileTest : SysuiTestCase() {
+class DndTileTest(flags: FlagsParameterization) : SysuiTestCase() {
companion object {
private const val DEFAULT_USER = 0
private const val KEY = Settings.Secure.ZEN_DURATION
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
}
- @Mock
- private lateinit var qsHost: QSHost
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
- @Mock
- private lateinit var metricsLogger: MetricsLogger
+ @Mock private lateinit var qsHost: QSHost
- @Mock
- private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var metricsLogger: MetricsLogger
- @Mock
- private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
- @Mock
- private lateinit var qsLogger: QSLogger
+ @Mock private lateinit var activityStarter: ActivityStarter
- @Mock
- private lateinit var uiEventLogger: QsEventLogger
+ @Mock private lateinit var qsLogger: QSLogger
- @Mock
- private lateinit var zenModeController: ZenModeController
+ @Mock private lateinit var uiEventLogger: QsEventLogger
- @Mock
- private lateinit var sharedPreferences: SharedPreferences
+ @Mock private lateinit var zenModeController: ZenModeController
- @Mock
- private lateinit var mDialogTransitionAnimator: DialogTransitionAnimator
+ @Mock private lateinit var sharedPreferences: SharedPreferences
- @Mock
- private lateinit var hostDialog: Dialog
+ @Mock private lateinit var mDialogTransitionAnimator: DialogTransitionAnimator
- @Mock
- private lateinit var expandable: Expandable
+ @Mock private lateinit var hostDialog: Dialog
- @Mock
- private lateinit var controller: DialogTransitionAnimator.Controller
+ @Mock private lateinit var expandable: Expandable
+
+ @Mock private lateinit var controller: DialogTransitionAnimator.Controller
private lateinit var secureSettings: SecureSettings
private lateinit var testableLooper: TestableLooper
@@ -118,31 +122,32 @@
whenever(qsHost.userId).thenReturn(DEFAULT_USER)
- val wrappedContext = object : ContextWrapper(
- ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)
- ) {
- override fun getSharedPreferences(file: File?, mode: Int): SharedPreferences {
- return sharedPreferences
+ val wrappedContext =
+ object :
+ ContextWrapper(ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)) {
+ override fun getSharedPreferences(file: File?, mode: Int): SharedPreferences {
+ return sharedPreferences
+ }
}
- }
whenever(qsHost.context).thenReturn(wrappedContext)
whenever(expandable.dialogTransitionController(any())).thenReturn(controller)
- tile = DndTile(
- qsHost,
- uiEventLogger,
- testableLooper.looper,
- Handler(testableLooper.looper),
- FalsingManagerFake(),
- metricsLogger,
- statusBarStateController,
- activityStarter,
- qsLogger,
- zenModeController,
- sharedPreferences,
- secureSettings,
- mDialogTransitionAnimator
- )
+ tile =
+ DndTile(
+ qsHost,
+ uiEventLogger,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ FalsingManagerFake(),
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ zenModeController,
+ sharedPreferences,
+ secureSettings,
+ mDialogTransitionAnimator,
+ )
}
@After
@@ -222,7 +227,7 @@
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_dnd_icon_off))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_dnd_icon_off))
}
@Test
@@ -232,6 +237,14 @@
tile.handleUpdateState(state, /* arg= */ null)
- assertThat(state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_dnd_icon_on))
+ assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_dnd_icon_on))
+ }
+
+ private fun createExpectedIcon(resId: Int): QSTile.Icon {
+ return if (isEnabled) {
+ DrawableIconWithRes(mContext.getDrawable(resId), resId)
+ } else {
+ QSTileImpl.ResourceIcon.get(resId)
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
index 190d80f..5f63b15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
@@ -16,6 +16,10 @@
package com.android.systemui.qs.tiles;
+import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
+
+import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
+
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;
@@ -32,12 +36,12 @@
import android.content.pm.UserInfo;
import android.os.Handler;
import android.os.RemoteException;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.provider.Settings;
import android.service.dreams.IDreamManager;
import android.service.quicksettings.Tile;
import android.testing.TestableLooper;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
@@ -45,9 +49,11 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.flags.QsInCompose;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.res.R;
@@ -63,11 +69,21 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidJUnit4.class)
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class DreamTileTest extends SysuiTestCase {
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
+ }
+
@Mock
private ActivityStarter mActivityStarter;
@Mock
@@ -101,6 +117,11 @@
private final String mExpectedTileLabel = mContext.getResources().getString(
R.string.quick_settings_screensaver_label);
+ public DreamTileTest(FlagsParameterization flags) {
+ super();
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -246,13 +267,13 @@
dockIntent.putExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_DESK);
receiver.onReceive(mContext, dockIntent);
mTestableLooper.processAllMessages();
- assertEquals(QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_screen_saver),
+ assertEquals(createExpectedIcon(R.drawable.ic_qs_screen_saver),
dockedTile.getState().icon);
dockIntent.putExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED);
receiver.onReceive(mContext, dockIntent);
mTestableLooper.processAllMessages();
- assertEquals(QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_screen_saver_undocked),
+ assertEquals(createExpectedIcon(R.drawable.ic_qs_screen_saver_undocked),
dockedTile.getState().icon);
destroyTile(dockedTile);
@@ -268,6 +289,14 @@
mTestableLooper.processAllMessages();
}
+ private QSTile.Icon createExpectedIcon(int resId) {
+ if (QsInCompose.isEnabled()) {
+ return new QSTileImpl.DrawableIconWithRes(mContext.getDrawable(resId), resId);
+ } else {
+ return QSTileImpl.ResourceIcon.get(resId);
+ }
+ }
+
private DreamTile constructTileForTest(boolean dreamSupported,
boolean dreamOnlyEnabledForSystemUser) {
return new DreamTile(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
index 5bd6944..ba6c2dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
@@ -16,16 +16,20 @@
package com.android.systemui.qs.tiles;
+import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
+
+import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.os.Handler;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.service.quicksettings.Tile;
import android.testing.TestableLooper;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
@@ -38,6 +42,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.flags.QsInCompose;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.res.R;
@@ -54,11 +59,21 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-@RunWith(AndroidJUnit4.class)
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class HotspotTileTest extends SysuiTestCase {
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
+ }
+
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
@Mock
@@ -74,6 +89,11 @@
private HotspotTile mTile;
private QSTile.BooleanState mState = new QSTile.BooleanState();
+ public HotspotTileTest(FlagsParameterization flags) {
+ super();
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() throws Exception {
mTestableLooper = TestableLooper.get(this);
@@ -144,7 +164,7 @@
mTile.handleUpdateState(state, /* arg= */ null);
assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_hotspot_icon_off));
+ .isEqualTo(createExpectedIcon(R.drawable.qs_hotspot_icon_off));
}
@Test
@@ -156,7 +176,7 @@
mTile.handleUpdateState(state, /* arg= */ null);
assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_hotspot_icon_search));
+ .isEqualTo(createExpectedIcon(R.drawable.qs_hotspot_icon_search));
}
@Test
@@ -168,6 +188,14 @@
mTile.handleUpdateState(state, /* arg= */ null);
assertThat(state.icon)
- .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_hotspot_icon_on));
+ .isEqualTo(createExpectedIcon(R.drawable.qs_hotspot_icon_on));
+ }
+
+ private QSTile.Icon createExpectedIcon(int resId) {
+ if (QsInCompose.isEnabled()) {
+ return new QSTileImpl.DrawableIconWithRes(mContext.getDrawable(resId), resId);
+ } else {
+ return QSTileImpl.ResourceIcon.get(resId);
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
index 5ada2f3..b26f0a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
@@ -260,7 +260,8 @@
@Test
public void connectCarrierNetwork_mergedCarrierEntryCanConnect_connectAndCreateSysUiToast() {
- when(mTelephonyManager.isDataEnabled()).thenReturn(true);
+ InternetDialogController spyController = spy(mInternetDialogController);
+ when(spyController.isMobileDataEnabled()).thenReturn(true);
when(mKeyguardStateController.isUnlocked()).thenReturn(true);
when(mConnectivityManager.getActiveNetwork()).thenReturn(mNetwork);
when(mConnectivityManager.getNetworkCapabilities(mNetwork))
@@ -272,7 +273,7 @@
mTestableResources.addOverride(R.string.wifi_wont_autoconnect_for_now,
TOAST_MESSAGE_STRING);
- mInternetDialogController.connectCarrierNetwork();
+ spyController.connectCarrierNetwork();
verify(mMergedCarrierEntry).connect(null /* callback */, false /* showToast */);
verify(mToastFactory).createToast(any(), any(), eq(TOAST_MESSAGE_STRING), anyString(),
@@ -281,11 +282,12 @@
@Test
public void connectCarrierNetwork_mergedCarrierEntryCanConnect_doNothingWhenSettingsOff() {
- when(mTelephonyManager.isDataEnabled()).thenReturn(false);
-
+ InternetDialogController spyController = spy(mInternetDialogController);
+ when(spyController.isMobileDataEnabled()).thenReturn(false);
mTestableResources.addOverride(R.string.wifi_wont_autoconnect_for_now,
TOAST_MESSAGE_STRING);
- mInternetDialogController.connectCarrierNetwork();
+
+ spyController.connectCarrierNetwork();
verify(mMergedCarrierEntry, never()).connect(null /* callback */, false /* showToast */);
verify(mToastFactory, never()).createToast(any(), any(), anyString(), anyString(), anyInt(),
@@ -294,12 +296,13 @@
@Test
public void connectCarrierNetwork_mergedCarrierEntryCanConnect_doNothingWhenKeyguardLocked() {
- when(mTelephonyManager.isDataEnabled()).thenReturn(true);
+ InternetDialogController spyController = spy(mInternetDialogController);
+ when(spyController.isMobileDataEnabled()).thenReturn(true);
when(mKeyguardStateController.isUnlocked()).thenReturn(false);
mTestableResources.addOverride(R.string.wifi_wont_autoconnect_for_now,
TOAST_MESSAGE_STRING);
- mInternetDialogController.connectCarrierNetwork();
+ spyController.connectCarrierNetwork();
verify(mMergedCarrierEntry, never()).connect(null /* callback */, false /* showToast */);
verify(mToastFactory, never()).createToast(any(), any(), anyString(), anyString(), anyInt(),
@@ -308,7 +311,8 @@
@Test
public void connectCarrierNetwork_mergedCarrierEntryCanConnect_doNothingWhenMobileIsPrimary() {
- when(mTelephonyManager.isDataEnabled()).thenReturn(true);
+ InternetDialogController spyController = spy(mInternetDialogController);
+ when(spyController.isMobileDataEnabled()).thenReturn(true);
when(mKeyguardStateController.isUnlocked()).thenReturn(true);
when(mConnectivityManager.getActiveNetwork()).thenReturn(mNetwork);
when(mConnectivityManager.getNetworkCapabilities(mNetwork))
@@ -446,10 +450,9 @@
when(mWifiStateWorker.isWifiEnabled()).thenReturn(true);
spyController.onAccessPointsChanged(null /* accessPoints */);
- doReturn(SUB_ID2).when(spyController).getActiveAutoSwitchNonDdsSubId();
+ doReturn(SUB_ID).when(spyController).getActiveAutoSwitchNonDdsSubId();
doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState();
- doReturn(mServiceState).when(mTelephonyManager).getServiceState();
- doReturn(TelephonyManager.DATA_DISCONNECTED).when(mTelephonyManager).getDataState();
+ spyController.mSubIdServiceState.put(SUB_ID2, mServiceState);
assertFalse(TextUtils.equals(spyController.getSubtitleText(false),
getResourcesString("all_network_unavailable")));
@@ -469,8 +472,7 @@
spyController.onAccessPointsChanged(null /* accessPoints */);
doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState();
- doReturn(mServiceState).when(mTelephonyManager).getServiceState();
- doReturn(TelephonyManager.DATA_DISCONNECTED).when(mTelephonyManager).getDataState();
+ spyController.mSubIdServiceState.put(SUB_ID, mServiceState);
assertTrue(TextUtils.equals(spyController.getSubtitleText(false),
getResourcesString("all_network_unavailable")));
@@ -487,11 +489,10 @@
fakeAirplaneModeEnabled(false);
when(mWifiStateWorker.isWifiEnabled()).thenReturn(true);
mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+ InternetDialogController spyController = spy(mInternetDialogController);
doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState();
- doReturn(mServiceState).when(mTelephonyManager).getServiceState();
-
- when(mTelephonyManager.isDataEnabled()).thenReturn(false);
+ spyController.mSubIdServiceState.put(SUB_ID, mServiceState);
assertThat(mInternetDialogController.getSubtitleText(false))
.isEqualTo(getResourcesString("non_carrier_network_unavailable"));
@@ -499,6 +500,9 @@
// if the Wi-Fi disallow config, then don't return Wi-Fi related string.
mInternetDialogController.mCanConfigWifi = false;
+ when(spyController.isMobileDataEnabled()).thenReturn(false);
+
+
assertThat(mInternetDialogController.getSubtitleText(false))
.isNotEqualTo(getResourcesString("non_carrier_network_unavailable"));
}
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 e7fb470c..c410111 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -33,7 +33,7 @@
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.Flags
-import com.android.systemui.Flags.FLAG_COMMUNAL_HUB_ON_MOBILE
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX
import com.android.systemui.SysuiTestCase
import com.android.systemui.ambient.touch.TouchHandler
@@ -43,7 +43,9 @@
import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository
import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
+import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.ui.compose.CommunalContent
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
@@ -131,20 +133,26 @@
underTest =
GlanceableHubContainerController(
communalInteractor,
+ communalSettingsInteractor,
communalViewModel,
keyguardInteractor,
- kosmos.keyguardTransitionInteractor,
+ keyguardTransitionInteractor,
shadeInteractor,
powerManager,
communalColors,
ambientTouchComponentFactory,
communalContent,
- kosmos.sceneDataSourceDelegator,
- kosmos.notificationStackScrollLayoutController,
- kosmos.keyguardMediaController,
- kosmos.lockscreenSmartspaceController,
+ sceneDataSourceDelegator,
+ notificationStackScrollLayoutController,
+ keyguardMediaController,
+ lockscreenSmartspaceController,
logcatLogBuffer("GlanceableHubContainerControllerTest"),
)
+
+ // Make below last notification true by default or else touches will be ignored by
+ // default when the hub is not showing.
+ whenever(notificationStackScrollLayoutController.isBelowLastNotification(any(), any()))
+ .thenReturn(true)
}
testableLooper = TestableLooper.get(this)
@@ -178,6 +186,7 @@
underTest =
GlanceableHubContainerController(
communalInteractor,
+ kosmos.communalSettingsInteractor,
communalViewModel,
keyguardInteractor,
kosmos.keyguardTransitionInteractor,
@@ -207,6 +216,7 @@
val underTest =
GlanceableHubContainerController(
communalInteractor,
+ kosmos.communalSettingsInteractor,
communalViewModel,
keyguardInteractor,
kosmos.keyguardTransitionInteractor,
@@ -231,6 +241,7 @@
val underTest =
GlanceableHubContainerController(
communalInteractor,
+ kosmos.communalSettingsInteractor,
communalViewModel,
keyguardInteractor,
kosmos.keyguardTransitionInteractor,
@@ -631,7 +642,7 @@
}
}
- @DisableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE)
+ @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
@Test
fun onTouchEvent_shadeInteracting_movesNotDispatched() =
with(kosmos) {
@@ -688,7 +699,7 @@
}
}
- @DisableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE)
+ @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
@Test
fun onTouchEvent_bouncerInteracting_movesNotDispatched() =
with(kosmos) {
@@ -721,11 +732,13 @@
}
}
- @EnableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE)
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
@Test
fun onTouchEvent_onLockscreenAndGlanceableHubV2_touchIgnored() =
with(kosmos) {
testScope.runTest {
+ kosmos.setCommunalV2ConfigEnabled(true)
+
// On lockscreen.
goToScene(CommunalScenes.Blank)
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 6912eda..d157cf2d 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
@@ -36,8 +36,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static kotlinx.coroutines.flow.FlowKt.flowOf;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -56,6 +54,8 @@
import static java.util.Collections.emptySet;
+import static kotlinx.coroutines.flow.FlowKt.flowOf;
+
import android.app.ActivityManager;
import android.app.IWallpaperManager;
import android.app.NotificationManager;
@@ -138,6 +138,7 @@
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.qs.flags.QSComposeFragment;
import com.android.systemui.res.R;
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.scene.domain.startable.ScrimStartable;
@@ -181,6 +182,7 @@
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.AvalancheProvider;
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
@@ -198,7 +200,6 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.window.StatusBarWindowController;
@@ -217,10 +218,6 @@
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.startingsurface.StartingSurface;
-import dagger.Lazy;
-
-import kotlinx.coroutines.test.TestScope;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -237,6 +234,9 @@
import javax.inject.Provider;
+import dagger.Lazy;
+import kotlinx.coroutines.test.TestScope;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
@RunWithLooper(setAsMainLooper = true)
@@ -667,6 +667,9 @@
mCentralSurfaces.startKeyguard();
mInitController.executePostInitTasks();
mCentralSurfaces.registerCallbacks();
+ // Clear first invocations caused by registering flows with JavaAdapter
+ mTestScope.getTestScheduler().runCurrent();
+ clearInvocations(mScrimController);
}
@Test
@@ -1159,8 +1162,7 @@
@Test
@EnableSceneContainer
- public void brightnesShowingChanged_flagEnabled_ScrimControllerNotified() {
- mCentralSurfaces.registerCallbacks();
+ public void brightnesShowingChanged_sceneContainerFlagEnabled_ScrimControllerNotified() {
final ScrimStartable scrimStartable = mKosmos.getScrimStartable();
scrimStartable.start();
@@ -1178,9 +1180,25 @@
@Test
@DisableSceneContainer
- public void brightnesShowingChanged_flagDisabled_ScrimControllerNotified() {
- mCentralSurfaces.registerCallbacks();
+ @EnableFlags(QSComposeFragment.FLAG_NAME)
+ public void brightnesShowingChanged_qsUiRefactorFlagEnabled_ScrimControllerNotified() {
+ mBrightnessMirrorShowingInteractor.setMirrorShowing(true);
+ mTestScope.getTestScheduler().runCurrent();
+ verify(mScrimController, atLeastOnce()).legacyTransitionTo(ScrimState.BRIGHTNESS_MIRROR);
+ clearInvocations(mScrimController);
+ 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, atLeastOnce()).legacyTransitionTo(captor.capture(), any());
+ assertThat(captor.getValue()).isNotEqualTo(ScrimState.BRIGHTNESS_MIRROR);
+ }
+
+ @Test
+ @DisableSceneContainer
+ @DisableFlags(QSComposeFragment.FLAG_NAME)
+ public void brightnesShowingChanged_flagsDisabled_ScrimControllerNotified() {
mBrightnessMirrorShowingInteractor.setMirrorShowing(true);
mTestScope.getTestScheduler().runCurrent();
verify(mScrimController, never()).legacyTransitionTo(ScrimState.BRIGHTNESS_MIRROR);
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 7634490..728f418 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
@@ -655,14 +655,14 @@
// CDMA roaming is off, GSM roaming is off
whenever(telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber).thenReturn(ERI_OFF)
cb.onDisplayInfoChanged(
- TelephonyDisplayInfo(NETWORK_TYPE_LTE, NETWORK_TYPE_UNKNOWN, false)
+ TelephonyDisplayInfo(NETWORK_TYPE_LTE, NETWORK_TYPE_UNKNOWN, false, false, false)
)
assertThat(latest).isFalse()
// CDMA roaming is off, GSM roaming is on
cb.onDisplayInfoChanged(
- TelephonyDisplayInfo(NETWORK_TYPE_LTE, NETWORK_TYPE_UNKNOWN, true)
+ TelephonyDisplayInfo(NETWORK_TYPE_LTE, NETWORK_TYPE_UNKNOWN, true, false, false)
)
assertThat(latest).isTrue()
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 df561ab..192d66c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -113,7 +113,7 @@
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.FakeFeatureFlagsClassic;
import com.android.systemui.flags.SceneContainerFlagParameterizationKt;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.kosmos.KosmosJavaAdapter;
@@ -368,7 +368,7 @@
private TestableLooper mTestableLooper;
private final FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
- private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
+ private final FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic();
private UserHandle mUser0;
diff --git a/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt b/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
index cc0597b..76fc611 100644
--- a/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
+++ b/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
@@ -30,6 +30,7 @@
import android.os.IBinder
import android.os.UserHandle
import android.util.ArrayMap
+import android.view.Display
import android.view.KeyEvent
import com.android.internal.logging.InstanceId
import com.android.internal.statusbar.IAddTileResultCallback
@@ -95,6 +96,90 @@
)
)
+ var statusBarIconsSecondaryDisplay =
+ ArrayMap<String, StatusBarIcon>().also {
+ it["slot1"] = mock<StatusBarIcon>()
+ it["slot2"] = mock<StatusBarIcon>()
+ }
+ var disabledFlags1SecondaryDisplay = 12345678
+ var appearanceSecondaryDisplay = 1234
+ var appearanceRegionsSecondaryDisplay =
+ arrayOf(
+ AppearanceRegion(
+ /* appearance = */ 123,
+ /* bounds = */ Rect(/* left= */ 4, /* top= */ 3, /* right= */ 2, /* bottom= */ 1),
+ ),
+ AppearanceRegion(
+ /* appearance = */ 345,
+ /* bounds = */ Rect(/* left= */ 1, /* top= */ 2, /* right= */ 3, /* bottom= */ 4),
+ ),
+ )
+ var imeWindowVisSecondaryDisplay = 9876
+ var imeBackDispositionSecondaryDisplay = 654
+ var showImeSwitcherSecondaryDisplay = true
+ var disabledFlags2SecondaryDisplay = 87654321
+ var navbarColorManagedByImeSecondaryDisplay = true
+ var behaviorSecondaryDisplay = 234
+ var requestedVisibleTypesSecondaryDisplay = 345
+ var packageNameSecondaryDisplay = "fake.bar.ser.vice"
+ var transientBarTypesSecondaryDisplay = 0
+ var letterboxDetailsSecondaryDisplay =
+ arrayOf(
+ LetterboxDetails(
+ /* letterboxInnerBounds = */ Rect(
+ /* left= */ 5,
+ /* top= */ 6,
+ /* right= */ 7,
+ /* bottom= */ 8,
+ ),
+ /* letterboxFullBounds = */ Rect(
+ /* left= */ 1,
+ /* top= */ 2,
+ /* right= */ 3,
+ /* bottom= */ 4,
+ ),
+ /* appAppearance = */ 123,
+ )
+ )
+
+ private val defaultRegisterStatusBarResult
+ get() =
+ RegisterStatusBarResult(
+ statusBarIcons,
+ disabledFlags1,
+ appearance,
+ appearanceRegions,
+ imeWindowVis,
+ imeBackDisposition,
+ showImeSwitcher,
+ disabledFlags2,
+ navbarColorManagedByIme,
+ behavior,
+ requestedVisibleTypes,
+ packageName,
+ transientBarTypes,
+ letterboxDetails,
+ )
+
+ private val registerStatusBarResultSecondaryDisplay
+ get() =
+ RegisterStatusBarResult(
+ statusBarIconsSecondaryDisplay,
+ disabledFlags1SecondaryDisplay,
+ appearanceSecondaryDisplay,
+ appearanceRegionsSecondaryDisplay,
+ imeWindowVisSecondaryDisplay,
+ imeBackDispositionSecondaryDisplay,
+ showImeSwitcherSecondaryDisplay,
+ disabledFlags2SecondaryDisplay,
+ navbarColorManagedByImeSecondaryDisplay,
+ behaviorSecondaryDisplay,
+ requestedVisibleTypesSecondaryDisplay,
+ packageNameSecondaryDisplay,
+ transientBarTypesSecondaryDisplay,
+ letterboxDetailsSecondaryDisplay,
+ )
+
override fun expandNotificationsPanel() {}
override fun collapsePanels() {}
@@ -140,21 +225,16 @@
override fun registerStatusBar(callbacks: IStatusBar): RegisterStatusBarResult {
registeredStatusBar = callbacks
- return RegisterStatusBarResult(
- statusBarIcons,
- disabledFlags1,
- appearance,
- appearanceRegions,
- imeWindowVis,
- imeBackDisposition,
- showImeSwitcher,
- disabledFlags2,
- navbarColorManagedByIme,
- behavior,
- requestedVisibleTypes,
- packageName,
- transientBarTypes,
- letterboxDetails,
+ return defaultRegisterStatusBarResult
+ }
+
+ override fun registerStatusBarForAllDisplays(
+ callbacks: IStatusBar
+ ): Map<String, RegisterStatusBarResult> {
+ registeredStatusBar = callbacks
+ return mapOf(
+ DEFAULT_DISPLAY_ID.toString() to defaultRegisterStatusBarResult,
+ SECONDARY_DISPLAY_ID.toString() to registerStatusBarResultSecondaryDisplay,
)
}
@@ -352,4 +432,9 @@
override fun unregisterNearbyMediaDevicesProvider(provider: INearbyMediaDevicesProvider) {}
override fun showRearDisplayDialog(currentBaseState: Int) {}
+
+ companion object {
+ const val DEFAULT_DISPLAY_ID = Display.DEFAULT_DISPLAY
+ const val SECONDARY_DISPLAY_ID = 2
+ }
}
diff --git a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FingerprintManagerKosmos.kt
similarity index 63%
copy from core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
copy to packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FingerprintManagerKosmos.kt
index 0589bf8..470a8e4 100644
--- a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FingerprintManagerKosmos.kt
@@ -5,7 +5,7 @@
* you may not use this 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,
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package android.app.ondeviceintelligence;
+package com.android.systemui.biometrics
-/**
- * @hide
- */
-parcelable FeatureDetails;
+import android.hardware.fingerprint.FingerprintManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.fingerprintManager by Kosmos.Fixture { mock<FingerprintManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt
index ae592b9..646c190 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt
@@ -17,20 +17,19 @@
package com.android.systemui.biometrics.domain.interactor
import android.content.applicationContext
-import android.hardware.fingerprint.FingerprintManager
import com.android.systemui.biometrics.authController
+import com.android.systemui.biometrics.fingerprintManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.user.domain.interactor.selectedUserInteractor
-import com.android.systemui.util.mockito.mock
val Kosmos.udfpsOverlayInteractor by Fixture {
UdfpsOverlayInteractor(
context = applicationContext,
authController = authController,
selectedUserInteractor = selectedUserInteractor,
- fingerprintManager = mock<FingerprintManager>(),
+ fingerprintManager = fingerprintManager,
scope = applicationCoroutineScope,
)
}
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 1f68195..ad92b31 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
@@ -16,6 +16,7 @@
package com.android.systemui.communal.domain.interactor
+import android.content.testableContext
import android.os.userManager
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.communal.data.repository.communalMediaRepository
@@ -34,6 +35,7 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.plugins.activityStarter
+import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.settings.userTracker
import com.android.systemui.statusbar.phone.fakeManagedProfileController
@@ -67,6 +69,13 @@
val Kosmos.editWidgetsActivityStarter by Fixture<EditWidgetsActivityStarter> { mock() }
+fun Kosmos.setCommunalV2ConfigEnabled(enabled: Boolean) {
+ testableContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_glanceableHubEnabled,
+ enabled,
+ )
+}
+
suspend fun Kosmos.setCommunalEnabled(enabled: Boolean) {
fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, enabled)
if (enabled) {
@@ -76,6 +85,15 @@
}
}
+suspend fun Kosmos.setCommunalV2Enabled(enabled: Boolean) {
+ setCommunalV2ConfigEnabled(true)
+ if (enabled) {
+ fakeUserRepository.asMainUser()
+ } else {
+ fakeUserRepository.asDefaultUser()
+ }
+}
+
suspend fun Kosmos.setCommunalAvailable(available: Boolean) {
setCommunalEnabled(available)
with(fakeKeyguardRepository) {
@@ -83,3 +101,12 @@
setKeyguardShowing(available)
}
}
+
+suspend fun Kosmos.setCommunalV2Available(available: Boolean) {
+ setCommunalV2ConfigEnabled(true)
+ setCommunalEnabled(available)
+ with(fakeKeyguardRepository) {
+ setIsEncryptedOrLockdown(!available)
+ setKeyguardShowing(available)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
similarity index 60%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
index b8d3ff4..b407b1b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
@@ -14,16 +14,18 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.ui.viewmodel
+package com.android.systemui.communal.ui.viewmodel
-import com.android.systemui.classifier.domain.interactor.falsingInteractor
+import android.service.dream.dreamManager
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.statusbar.policy.batteryController
-val Kosmos.editModeButtonViewModelFactory by
+val Kosmos.communalToDreamButtonViewModel by
Kosmos.Fixture {
- object : EditModeButtonViewModel.Factory {
- override fun create(): EditModeButtonViewModel {
- return EditModeButtonViewModel(editModeViewModel, falsingInteractor)
- }
- }
+ CommunalToDreamButtonViewModel(
+ backgroundContext = testDispatcher,
+ batteryController = batteryController,
+ dreamManager = dreamManager,
+ )
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryKosmos.kt
index 1df3ef4..1021169 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryKosmos.kt
@@ -17,9 +17,11 @@
package com.android.systemui.education.data.repository
import com.android.systemui.kosmos.Kosmos
+import java.time.Duration
import java.time.Instant
var Kosmos.contextualEducationRepository: FakeContextualEducationRepository by
Kosmos.Fixture { FakeContextualEducationRepository() }
-var Kosmos.fakeEduClock: FakeEduClock by Kosmos.Fixture { FakeEduClock(Instant.MIN) }
+var Kosmos.fakeEduClock: FakeEduClock by
+ Kosmos.Fixture { FakeEduClock(Instant.ofEpochSecond(Duration.ofDays(30).seconds)) }
diff --git a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl b/packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/GlobalActionsDialogLiteKosmos.kt
similarity index 66%
copy from core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
copy to packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/GlobalActionsDialogLiteKosmos.kt
index 0589bf8..63bfa52 100644
--- a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/GlobalActionsDialogLiteKosmos.kt
@@ -5,7 +5,7 @@
* you may not use this 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,
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package android.app.ondeviceintelligence;
+package com.android.systemui.globalactions
-/**
- * @hide
- */
-parcelable FeatureDetails;
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+/** Provides a mock */
+val Kosmos.globalActionsDialogLite: GlobalActionsDialogLite by Kosmos.Fixture { mock() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index 1828da5..4cb8a41 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -25,6 +25,7 @@
import com.android.systemui.keyboard.shortcut.data.repository.CustomInputGesturesRepository
import com.android.systemui.keyboard.shortcut.data.repository.CustomShortcutCategoriesRepository
import com.android.systemui.keyboard.shortcut.data.repository.DefaultShortcutCategoriesRepository
+import com.android.systemui.keyboard.shortcut.data.repository.InputGestureDataAdapter
import com.android.systemui.keyboard.shortcut.data.repository.InputGestureMaps
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutCategoriesUtils
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository
@@ -38,6 +39,7 @@
import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutCustomizationInteractor
import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperCategoriesInteractor
import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperStateInteractor
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperExclusions
import com.android.systemui.keyboard.shortcut.ui.ShortcutCustomizationDialogStarter
import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutCustomizationViewModel
import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutHelperViewModel
@@ -80,12 +82,16 @@
var Kosmos.shortcutHelperCurrentAppShortcutsSource: KeyboardShortcutGroupsSource by
Kosmos.Fixture { CurrentAppShortcutsSource(windowManager) }
+val Kosmos.shortcutHelperExclusions by
+ Kosmos.Fixture { ShortcutHelperExclusions(applicationContext) }
+
val Kosmos.shortcutCategoriesUtils by
Kosmos.Fixture {
ShortcutCategoriesUtils(
applicationContext,
backgroundCoroutineContext,
fakeInputManager.inputManager,
+ shortcutHelperExclusions,
)
}
@@ -107,9 +113,10 @@
val Kosmos.inputGestureMaps by Kosmos.Fixture { InputGestureMaps(applicationContext) }
-val Kosmos.customInputGesturesRepository by Kosmos.Fixture {
- CustomInputGesturesRepository(userTracker, testDispatcher)
-}
+val Kosmos.inputGestureDataAdapter by Kosmos.Fixture { InputGestureDataAdapter(userTracker, inputGestureMaps, applicationContext)}
+
+val Kosmos.customInputGesturesRepository by
+ Kosmos.Fixture { CustomInputGesturesRepository(userTracker, testDispatcher) }
val Kosmos.customShortcutCategoriesRepository by
Kosmos.Fixture {
@@ -118,10 +125,9 @@
applicationCoroutineScope,
testDispatcher,
shortcutCategoriesUtils,
- applicationContext,
- inputGestureMaps,
+ inputGestureDataAdapter,
customInputGesturesRepository,
- fakeInputManager.inputManager
+ fakeInputManager.inputManager,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
index 1556058..7ee9d84 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
@@ -8,7 +8,10 @@
import com.android.systemui.settings.brightness.ui.BrightnessWarningToast
import com.android.systemui.util.mockito.mock
import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -58,3 +61,27 @@
fun <T> Kosmos.collectLastValue(flow: Flow<T>) = testScope.collectLastValue(flow)
fun <T> Kosmos.collectValues(flow: Flow<T>): FlowValue<List<T>> = testScope.collectValues(flow)
+
+/**
+ * Retrieve the current value of this [StateFlow] safely. Needs a [TestScope] in order to make sure
+ * that all pending tasks have run before returning a value. Tests that directly access
+ * [StateFlow.value] may be incorrect, since the value returned may be stale if the current test
+ * dispatcher is a [StandardTestDispatcher].
+ *
+ * If you want to assert on a [Flow] that is not a [StateFlow], please use
+ * [TestScope.collectLastValue], to make sure that the desired value is captured when emitted.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+fun <T> TestScope.currentValue(stateFlow: StateFlow<T>): T {
+ val values = mutableListOf<T>()
+ val job = backgroundScope.launch { stateFlow.collect(values::add) }
+ runCurrent()
+ job.cancel()
+ // StateFlow should always have at least one value
+ return values.last()
+}
+
+/** Retrieve the current value of this [StateFlow] safely. See `currentValue(TestScope)`. */
+fun <T> Kosmos.currentValue(stateFlow: StateFlow<T>): T {
+ return testScope.currentValue(stateFlow)
+}
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 7a04aa2..7964c11 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
@@ -19,7 +19,6 @@
import android.content.applicationContext
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.media.controls.util.mediaSmartspaceLogger
-import com.android.systemui.statusbar.policy.configurationController
import com.android.systemui.util.time.systemClock
val Kosmos.mediaFilterRepository by
@@ -27,7 +26,6 @@
MediaFilterRepository(
applicationContext = applicationContext,
systemClock = systemClock,
- configurationController = configurationController,
smartspaceLogger = mediaSmartspaceLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
index 10b073e..2e6d8ed 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
@@ -28,6 +28,7 @@
import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.FakeStatusBarStateController
import com.android.systemui.statusbar.StatusBarStateControllerImpl
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.util.mockito.mock
@@ -49,3 +50,6 @@
{ alternateBouncerInteractor },
)
}
+
+var Kosmos.fakeStatusBarStateController: SysuiStatusBarStateController by
+ Kosmos.Fixture { FakeStatusBarStateController() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
index c574463..d71bc31 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
@@ -33,6 +33,7 @@
import com.android.systemui.qs.panels.ui.viewmodel.inFirstPageViewModel
import com.android.systemui.qs.panels.ui.viewmodel.mediaInRowInLandscapeViewModelFactory
import com.android.systemui.qs.ui.viewmodel.quickSettingsContainerViewModelFactory
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.largeScreenHeaderHelper
import com.android.systemui.shade.transition.largeScreenShadeInterpolator
import com.android.systemui.statusbar.disableflags.domain.interactor.disableFlagsInteractor
@@ -56,6 +57,7 @@
disableFlagsInteractor,
keyguardTransitionInteractor,
largeScreenShadeInterpolator,
+ shadeInteractor,
configurationInteractor,
largeScreenHeaderHelper,
tileSquishinessInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepositoryKosmos.kt
index 4acedaa..322b412 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepositoryKosmos.kt
@@ -18,5 +18,4 @@
import com.android.systemui.kosmos.Kosmos
-var Kosmos.gridLayoutTypeRepository: GridLayoutTypeRepository by
- Kosmos.Fixture { GridLayoutTypeRepositoryImpl() }
+var Kosmos.gridLayoutTypeRepository by Kosmos.Fixture { GridLayoutTypeRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorKosmos.kt
index c951642..40c3c96 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorKosmos.kt
@@ -20,10 +20,19 @@
import com.android.systemui.qs.panels.data.repository.gridLayoutTypeRepository
import com.android.systemui.qs.panels.shared.model.GridLayoutType
import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
+import com.android.systemui.qs.panels.shared.model.PaginatedGridLayoutType
import com.android.systemui.qs.panels.ui.compose.GridLayout
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.infiniteGridLayout
+import com.android.systemui.qs.panels.ui.compose.paginatedGridLayout
+import com.android.systemui.shade.domain.interactor.shadeModeInteractor
val Kosmos.gridLayoutTypeInteractor by
- Kosmos.Fixture { GridLayoutTypeInteractor(gridLayoutTypeRepository) }
+ Kosmos.Fixture { GridLayoutTypeInteractor(gridLayoutTypeRepository, shadeModeInteractor) }
val Kosmos.gridLayoutMap: Map<GridLayoutType, GridLayout> by
- Kosmos.Fixture { mapOf(Pair(InfiniteGridLayoutType, infiniteGridLayout)) }
+ Kosmos.Fixture {
+ mapOf(
+ Pair(InfiniteGridLayoutType, infiniteGridLayout),
+ Pair(PaginatedGridLayoutType, paginatedGridLayout),
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayoutKosmos.kt
similarity index 61%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayoutKosmos.kt
index b8d3ff4..f412d98 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayoutKosmos.kt
@@ -14,16 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.ui.viewmodel
+package com.android.systemui.qs.panels.ui.compose
-import com.android.systemui.classifier.domain.interactor.falsingInteractor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.infiniteGridLayout
+import com.android.systemui.qs.panels.ui.viewmodel.paginatedGridViewModelFactory
-val Kosmos.editModeButtonViewModelFactory by
- Kosmos.Fixture {
- object : EditModeButtonViewModel.Factory {
- override fun create(): EditModeButtonViewModel {
- return EditModeButtonViewModel(editModeViewModel, falsingInteractor)
- }
- }
- }
+val Kosmos.paginatedGridLayout by
+ Kosmos.Fixture { PaginatedGridLayout(paginatedGridViewModelFactory, infiniteGridLayout) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayoutKosmos.kt
similarity index 89%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayoutKosmos.kt
index 6fe860c..f57806c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayoutKosmos.kt
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.qs.panels.ui.compose.infinitegrid
import com.android.systemui.haptics.msdl.tileHapticsViewModelFactoryProvider
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.ui.compose.infinitegrid.InfiniteGridLayout
import com.android.systemui.qs.panels.ui.viewmodel.detailsViewModel
import com.android.systemui.qs.panels.ui.viewmodel.iconTilesViewModel
import com.android.systemui.qs.panels.ui.viewmodel.infiniteGridViewModelFactory
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt
index 33227a4..86c3add 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt
@@ -23,8 +23,8 @@
import com.android.systemui.qs.panels.domain.interactor.editTilesListInteractor
import com.android.systemui.qs.panels.domain.interactor.gridLayoutMap
import com.android.systemui.qs.panels.domain.interactor.gridLayoutTypeInteractor
-import com.android.systemui.qs.panels.domain.interactor.infiniteGridLayout
import com.android.systemui.qs.panels.domain.interactor.tilesAvailabilityInteractor
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.infiniteGridLayout
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
import com.android.systemui.qs.pipeline.domain.interactor.minimumTilesInteractor
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
index 5c71ba2..128cfca 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
@@ -20,6 +20,7 @@
import com.android.systemui.development.ui.viewmodel.buildNumberViewModelFactory
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.panels.domain.interactor.paginatedGridInteractor
+import com.android.systemui.qs.panels.ui.viewmodel.toolbar.editModeButtonViewModelFactory
val Kosmos.paginatedGridViewModel by
Kosmos.Fixture {
@@ -33,3 +34,10 @@
falsingInteractor,
)
}
+
+val Kosmos.paginatedGridViewModelFactory by
+ Kosmos.Fixture {
+ object : PaginatedGridViewModel.Factory {
+ override fun create() = paginatedGridViewModel
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModelKosmos.kt
index 9481fca..e79d213 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModelKosmos.kt
@@ -20,7 +20,7 @@
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.qs.panels.domain.interactor.gridLayoutMap
import com.android.systemui.qs.panels.domain.interactor.gridLayoutTypeInteractor
-import com.android.systemui.qs.panels.domain.interactor.infiniteGridLayout
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.infiniteGridLayout
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
val Kosmos.tileGridViewModel by
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModelKosmos.kt
similarity index 88%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModelKosmos.kt
index b8d3ff4..8ae1332 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModelKosmos.kt
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.ui.viewmodel
+package com.android.systemui.qs.panels.ui.viewmodel.toolbar
import com.android.systemui.classifier.domain.interactor.falsingInteractor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel
val Kosmos.editModeButtonViewModelFactory by
Kosmos.Fixture {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModelKosmos.kt
new file mode 100644
index 0000000..52d8a3a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModelKosmos.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel.toolbar
+
+import android.content.applicationContext
+import com.android.systemui.classifier.domain.interactor.falsingInteractor
+import com.android.systemui.globalactions.globalActionsDialogLite
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.footerActionsInteractor
+
+val Kosmos.toolbarViewModelFactory by
+ Kosmos.Fixture {
+ object : ToolbarViewModel.Factory {
+ override fun create(): ToolbarViewModel {
+ return ToolbarViewModel(
+ editModeButtonViewModelFactory,
+ footerActionsInteractor,
+ { globalActionsDialogLite },
+ falsingInteractor,
+ applicationContext,
+ )
+ }
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt
index 597d52d..bc1c60c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt
@@ -16,6 +16,8 @@
package com.android.systemui.qs.tiles.base.interactor
+import com.android.systemui.plugins.qs.TileDetailsViewModel
+import com.android.systemui.qs.FakeTileDetailsViewModel
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
@@ -31,4 +33,7 @@
override suspend fun handleInput(input: QSTileInput<T>) {
mutex.withLock { mutableInputs.add(input) }
}
+
+ override var detailsViewModel: TileDetailsViewModel? =
+ FakeTileDetailsViewModel("FakeQSTileUserActionInteractor")
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt
index 2ecfb45..3c37101 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt
@@ -18,6 +18,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
+import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.statusbar.policy.ui.dialog.modesDialogDelegate
import javax.inject.Provider
@@ -26,5 +27,6 @@
ModesTileUserActionInteractor(
qsTileIntentUserInputHandler,
Provider { modesDialogDelegate }.get(),
+ zenModeInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt
index 6afc0d80..aa6ce9a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt
@@ -22,6 +22,7 @@
import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.quickQuickSettingsViewModelFactory
import com.android.systemui.qs.panels.ui.viewmodel.tileGridViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.toolbar.toolbarViewModelFactory
val Kosmos.quickSettingsContainerViewModelFactory by
Kosmos.Fixture {
@@ -36,6 +37,7 @@
tileGridViewModel,
editModeViewModel,
detailsViewModel,
+ toolbarViewModelFactory,
)
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
index 8712b02..22f8767 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
@@ -34,12 +34,12 @@
const val DISPLAY_ID = Display.DEFAULT_DISPLAY
}
- override val defaultDisplay: FakeStatusBarModePerDisplayRepository =
- FakeStatusBarModePerDisplayRepository()
+ private val perDisplayRepos = mutableMapOf<Int, FakeStatusBarModePerDisplayRepository>()
- override fun forDisplay(displayId: Int): FakeStatusBarModePerDisplayRepository {
- return defaultDisplay
- }
+ override val defaultDisplay: FakeStatusBarModePerDisplayRepository = forDisplay(DISPLAY_ID)
+
+ override fun forDisplay(displayId: Int): FakeStatusBarModePerDisplayRepository =
+ perDisplayRepos.computeIfAbsent(displayId) { FakeStatusBarModePerDisplayRepository() }
}
class FakeStatusBarModePerDisplayRepository : StatusBarModePerDisplayRepository {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerKosmos.kt
new file mode 100644
index 0000000..fa5bd7a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerKosmos.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.plugins.statusbar.statusBarStateController
+import com.android.systemui.statusbar.notificationLockscreenUserManager
+import com.android.systemui.statusbar.policy.keyguardStateController
+import org.mockito.kotlin.mock
+
+var Kosmos.dynamicPrivacyController: DynamicPrivacyController by
+ Kosmos.Fixture {
+ DynamicPrivacyController(
+ notificationLockscreenUserManager,
+ keyguardStateController,
+ statusBarStateController,
+ )
+ }
+
+var Kosmos.mockDynamicPrivacyController: DynamicPrivacyController by Kosmos.Fixture { mock() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorKosmos.kt
new file mode 100644
index 0000000..88bf9a5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorKosmos.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.statusbar.notification.collection.coordinator
+
+import com.android.keyguard.keyguardUpdateMonitor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.statusbar.statusBarStateController
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.statusbar.notification.dynamicPrivacyController
+import com.android.systemui.statusbar.notificationLockscreenUserManager
+import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.statusbar.policy.sensitiveNotificationProtectionController
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@OptIn(ExperimentalCoroutinesApi::class)
+var Kosmos.sensitiveContentCoordinator: SensitiveContentCoordinator by
+ Kosmos.Fixture {
+ SensitiveContentCoordinatorImpl(
+ dynamicPrivacyController,
+ notificationLockscreenUserManager,
+ keyguardUpdateMonitor,
+ statusBarStateController,
+ keyguardStateController,
+ selectedUserInteractor,
+ sensitiveNotificationProtectionController,
+ deviceEntryInteractor,
+ sceneInteractor,
+ testScope.backgroundScope,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
index 2d3f68f..7126933 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
@@ -34,7 +34,7 @@
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.media.controls.util.MediaFeatureFlag
@@ -92,8 +92,6 @@
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
-import com.android.systemui.wmshell.BubblesManager
-import java.util.Optional
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executor
import java.util.concurrent.TimeUnit
@@ -106,7 +104,7 @@
class ExpandableNotificationRowBuilder(
private val context: Context,
dependency: TestableDependency,
- private val featureFlags: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic(),
+ featureFlags: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic(),
) {
private val mMockLogger: ExpandableNotificationRowLogger
@@ -137,7 +135,7 @@
featureFlags.setDefault(Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE)
featureFlags.setDefault(Flags.BIGPICTURE_NOTIFICATION_LAZY_LOADING)
- dependency.injectTestDependency(FeatureFlags::class.java, featureFlags)
+ dependency.injectTestDependency(FeatureFlagsClassic::class.java, featureFlags)
dependency.injectMockDependency(NotificationMediaManager::class.java)
dependency.injectMockDependency(NotificationShadeWindowController::class.java)
dependency.injectMockDependency(MediaOutputDialogManager::class.java)
@@ -299,7 +297,7 @@
}
private fun getNotifRemoteViewsFactoryContainer(
- featureFlags: FeatureFlags
+ featureFlags: FeatureFlagsClassic
): NotifRemoteViewsFactoryContainer {
return NotifRemoteViewsFactoryContainerImpl(
featureFlags,
@@ -380,7 +378,6 @@
mStatusBarStateController,
mPeopleNotificationIdentifier,
mOnUserInteractionCallback,
- Optional.of(Mockito.mock(BubblesManager::class.java, STUB_ONLY)),
Mockito.mock(NotificationGutsManager::class.java, STUB_ONLY),
mDismissibilityProvider,
Mockito.mock(MetricsLogger::class.java, STUB_ONLY),
@@ -388,11 +385,10 @@
Mockito.mock(ColorUpdateLogger::class.java, STUB_ONLY),
mSmartReplyConstants,
mSmartReplyController,
- featureFlags,
Mockito.mock(IStatusBarService::class.java, STUB_ONLY),
Mockito.mock(UiEventLogger::class.java, STUB_ONLY),
)
- row.setAboveShelfChangedListener { aboveShelf: Boolean -> }
+ row.setAboveShelfChangedListener {}
mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags)
inflateAndWait(entry)
return row
@@ -410,7 +406,7 @@
private const val PKG = "com.android.systemui"
private const val UID = 1000
private val USER_HANDLE = UserHandle.of(ActivityManager.getCurrentUser())
- private val INFLATION_FLAGS =
+ private const val INFLATION_FLAGS =
FLAG_CONTENT_VIEW_CONTRACTED or FLAG_CONTENT_VIEW_EXPANDED or FLAG_CONTENT_VIEW_HEADS_UP
private const val IS_CONVERSATION_FLAG = "test.isConversation"
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModelBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModelBuilder.kt
index 3963d7c..766b280 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModelBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModelBuilder.kt
@@ -23,5 +23,6 @@
fun inCallModel(
startTimeMs: Long,
notificationIcon: StatusBarIconView? = null,
- intent: PendingIntent? = null
-) = OngoingCallModel.InCall(startTimeMs, notificationIcon, intent)
+ intent: PendingIntent? = null,
+ notificationKey: String = "test",
+) = OngoingCallModel.InCall(startTimeMs, notificationIcon, intent, notificationKey)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerKosmos.kt
similarity index 60%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerKosmos.kt
index b8d3ff4..8f9184d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerKosmos.kt
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.ui.viewmodel
+package com.android.systemui.statusbar.policy
-import com.android.systemui.classifier.domain.interactor.falsingInteractor
import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
-val Kosmos.editModeButtonViewModelFactory by
- Kosmos.Fixture {
- object : EditModeButtonViewModel.Factory {
- override fun create(): EditModeButtonViewModel {
- return EditModeButtonViewModel(editModeViewModel, falsingInteractor)
- }
- }
- }
+var Kosmos.sensitiveNotificationProtectionController: SensitiveNotificationProtectionController by
+ Kosmos.Fixture { mockSensitiveNotificationProtectionController }
+val Kosmos.mockSensitiveNotificationProtectionController:
+ SensitiveNotificationProtectionController by
+ Kosmos.Fixture { mock<SensitiveNotificationProtectionController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt
index 68d08e2..bbccbb1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt
@@ -20,6 +20,7 @@
import com.android.settingslib.notification.modes.zenIconLoader
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.backgroundScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.shared.notifications.data.repository.notificationSettingsRepository
import com.android.systemui.statusbar.policy.data.repository.deviceProvisioningRepository
@@ -32,6 +33,7 @@
zenModeRepository = zenModeRepository,
notificationSettingsRepository = notificationSettingsRepository,
bgDispatcher = testDispatcher,
+ backgroundScope = backgroundScope,
iconLoader = zenIconLoader,
deviceProvisioningRepository = deviceProvisioningRepository,
userSetupRepository = userSetupRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt
index c8ba551..34661ce 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt
@@ -21,6 +21,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.statusbar.notification.domain.interactor.notificationsSoundPolicyInteractor
import com.android.systemui.volume.dialog.domain.interactor.volumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.ringer.domain.volumeDialogRingerInteractor
import com.android.systemui.volume.dialog.shared.volumeDialogLogger
@@ -31,7 +32,8 @@
applicationContext = applicationContext,
backgroundDispatcher = testDispatcher,
coroutineScope = applicationCoroutineScope,
- interactor = volumeDialogRingerInteractor,
+ soundPolicyInteractor = notificationsSoundPolicyInteractor,
+ ringerInteractor = volumeDialogRingerInteractor,
vibrator = vibratorHelper,
volumeDialogLogger = volumeDialogLogger,
visibilityInteractor = volumeDialogVisibilityInteractor,
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java b/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java
index f024b5f..e50fc3a 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java
@@ -1228,10 +1228,7 @@
@VisibleForTesting(visibility = Visibility.PRIVATE)
void setSafeModeAlarm() {
- final boolean isFlagSafeModeConfigEnabled = mVcnContext.getFeatureFlags().safeModeConfig();
- logVdbg("isFlagSafeModeConfigEnabled " + isFlagSafeModeConfigEnabled);
-
- if (isFlagSafeModeConfigEnabled && !mConnectionConfig.isSafeModeEnabled()) {
+ if (!mConnectionConfig.isSafeModeEnabled()) {
logVdbg("setSafeModeAlarm: safe mode disabled");
return;
}
diff --git a/proto/Android.bp b/proto/Android.bp
index a5e1335..feaa6d2 100644
--- a/proto/Android.bp
+++ b/proto/Android.bp
@@ -25,6 +25,10 @@
static_libs: ["libprotobuf-java-nano"],
},
},
+ apex_available: [
+ "com.android.neuralnetworks",
+ "//apex_available:platform",
+ ],
}
java_library_static {
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 66c8d0f..59043a83 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -182,21 +182,6 @@
visibility: [":__subpackages__"],
}
-// Separated out from ravenwood-junit-impl since it needs to compile
-// against `module_current`
-java_library {
- name: "ravenwood-junit-impl-flag",
- srcs: [
- "junit-flag-src/**/*.java",
- ],
- sdk_version: "module_current",
- libs: [
- "junit",
- "flag-junit",
- ],
- visibility: ["//visibility:public"],
-}
-
// Carefully compiles against only module_current to support tests that
// want to verify they're unbundled. The "impl" library above is what
// ships inside the Ravenwood environment to actually drive any API
@@ -651,7 +636,6 @@
"flag-junit",
"ravenwood-framework",
"ravenwood-junit-impl",
- "ravenwood-junit-impl-flag",
"mockito-ravenwood-prebuilt",
"inline-mockito-ravenwood-prebuilt",
diff --git a/ravenwood/junit-flag-src/android/platform/test/flag/junit/RavenwoodFlagsValueProvider.java b/ravenwood/junit-flag-src/android/platform/test/flag/junit/RavenwoodFlagsValueProvider.java
deleted file mode 100644
index 9d62774..0000000
--- a/ravenwood/junit-flag-src/android/platform/test/flag/junit/RavenwoodFlagsValueProvider.java
+++ /dev/null
@@ -1,54 +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.platform.test.flag.junit;
-
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.IFlagsValueProvider;
-
-/**
- * Offer to create {@link CheckFlagsRule} instances that are useful on the Ravenwood deviceless
- * testing environment.
- *
- * At the moment, default flag values are not available on Ravenwood, so the only options offered
- * here are "all-on" and "all-off" options. Tests that want to exercise specific flag states should
- * use {@link android.platform.test.flag.junit.SetFlagsRule}.
- */
-public class RavenwoodFlagsValueProvider {
- /**
- * Create a {@link CheckFlagsRule} instance where flags are in an "all-on" state.
- */
- public static CheckFlagsRule createAllOnCheckFlagsRule() {
- return new CheckFlagsRule(new IFlagsValueProvider() {
- @Override
- public boolean getBoolean(String flag) {
- return true;
- }
- });
- }
-
- /**
- * Create a {@link CheckFlagsRule} instance where flags are in an "all-off" state.
- */
- public static CheckFlagsRule createAllOffCheckFlagsRule() {
- return new CheckFlagsRule(new IFlagsValueProvider() {
- @Override
- public boolean getBoolean(String flag) {
- return false;
- }
- });
- }
-}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
index 9644a52..3ebef02 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -129,7 +129,7 @@
mTestClass = new TestClass(testClass);
- Log.v(TAG, "RavenwoodAwareTestRunner starting for " + testClass.getCanonicalName());
+ Log.v(TAG, "RavenwoodAwareTestRunner initializing for " + testClass.getCanonicalName());
// Hook point to allow more customization.
runAnnotatedMethodsOnRavenwood(RavenwoodTestRunnerInitializing.class, null);
@@ -146,7 +146,9 @@
private void runAnnotatedMethodsOnRavenwood(Class<? extends Annotation> annotationClass,
Object instance) {
- Log.v(TAG, "runAnnotatedMethodsOnRavenwood() " + annotationClass.getName());
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ Log.v(TAG, "runAnnotatedMethodsOnRavenwood() " + annotationClass.getName());
+ }
for (var method : mTestClass.getAnnotatedMethods(annotationClass)) {
ensureIsPublicVoidMethod(method.getMethod(), /* isStatic=*/ instance == null);
@@ -169,12 +171,14 @@
RavenwoodTestStats.getInstance().attachToRunNotifier(notifier);
if (mRealRunner instanceof ClassSkippingTestRunner) {
- Log.i(TAG, "onClassSkipped: description=" + description);
+ Log.v(TAG, "onClassSkipped: description=" + description);
mRealRunner.run(notifier);
return;
}
- Log.v(TAG, "Starting " + mTestJavaClass.getCanonicalName());
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ Log.v(TAG, "Running " + mTestJavaClass.getCanonicalName());
+ }
if (RAVENWOOD_VERBOSE_LOGGING) {
dumpDescription(description);
}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
index 9eff20a..a332633 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
@@ -132,36 +132,27 @@
@Override
public Looper getMainLooper() {
- Objects.requireNonNull(mMainThread,
- "Test must request setProvideMainThread() via RavenwoodConfig");
return mMainThread.getLooper();
}
@Override
public Handler getMainThreadHandler() {
- Objects.requireNonNull(mMainThread,
- "Test must request setProvideMainThread() via RavenwoodConfig");
return mMainThread.getThreadHandler();
}
@Override
public Executor getMainExecutor() {
- Objects.requireNonNull(mMainThread,
- "Test must request setProvideMainThread() via RavenwoodConfig");
return mMainThread.getThreadExecutor();
}
@Override
public String getPackageName() {
- return Objects.requireNonNull(mPackageName,
- "Test must request setPackageName() (or setTargetPackageName())"
- + " via RavenwoodConfig");
+ return mPackageName;
}
@Override
public String getOpPackageName() {
- return Objects.requireNonNull(mPackageName,
- "Test must request setPackageName() via RavenwoodConfig");
+ return mPackageName;
}
@Override
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
index a5d0bfd..70bc52b 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
@@ -15,6 +15,8 @@
*/
package android.platform.test.ravenwood;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -59,16 +61,22 @@
private Description mMethodDescription;
public void enterTestRunner() {
- Log.i(TAG, "enterTestRunner: " + mRunner);
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ Log.v(TAG, "enterTestRunner: " + mRunner);
+ }
RavenwoodRuntimeEnvironmentController.initForRunner();
}
public void enterTestClass() {
- Log.i(TAG, "enterTestClass: " + mRunner.mTestJavaClass.getName());
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ Log.v(TAG, "enterTestClass: " + mRunner.mTestJavaClass.getName());
+ }
}
public void exitTestClass() {
- Log.i(TAG, "exitTestClass: " + mRunner.mTestJavaClass.getName());
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ Log.v(TAG, "exitTestClass: " + mRunner.mTestJavaClass.getName());
+ }
assertTrue(RAVENWOOD_RULE_ERROR, sActiveProperties.isEmpty());
RavenwoodRuntimeEnvironmentController.exitTestClass();
}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index 930914f..3cb6c5a 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -552,7 +552,7 @@
}
private static void dumpCommandLineArgs() {
- Log.i(TAG, "JVM arguments:");
+ Log.v(TAG, "JVM arguments:");
// Note, we use the wrapper in JUnit4, not the actual class (
// java.lang.management.ManagementFactory), because we can't see the later at the build
@@ -561,7 +561,7 @@
var args = ManagementFactory.getRuntimeMXBean().getInputArguments();
for (var arg : args) {
- Log.i(TAG, " " + arg);
+ Log.v(TAG, " " + arg);
}
}
}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
index fac0791..70c161c1 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
@@ -84,7 +84,7 @@
var ravenwoodProps = readProperties(path + RAVENWOOD_BUILD_PROP);
var deviceProps = readProperties(path + DEVICE_BUILD_PROP);
- Log.i(TAG, "Default system properties:");
+ Log.v(TAG, "Default system properties:");
ravenwoodProps.forEach((key, origValue) -> {
final String value;
@@ -100,7 +100,7 @@
} else {
value = origValue;
}
- Log.i(TAG, key + "=" + value);
+ Log.v(TAG, key + "=" + value);
sDefaultValues.put(key, value);
});
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java
index f3688d6..3592105 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java
@@ -15,6 +15,8 @@
*/
package android.platform.test.ravenwood;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
+
import android.platform.test.annotations.internal.InnerRunner;
import android.util.Log;
@@ -53,7 +55,9 @@
}
try {
- Log.i(TAG, "Initializing the inner runner: " + runnerClass);
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ Log.v(TAG, "Initializing the inner runner: " + runnerClass);
+ }
try {
return runnerClass.getConstructor(Class.class)
.newInstance(testClass.getJavaClass());
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
deleted file mode 100644
index 3ed0f50..0000000
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
+++ /dev/null
@@ -1,139 +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.platform.test.ravenwood;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * @deprecated This class will be removed. Reach out to g/ravenwood if you need any features in it.
- */
-@Deprecated
-public final class RavenwoodConfig {
- /**
- * Use this to mark a field as the configuration.
- * @hide
- */
- @Target({ElementType.FIELD})
- @Retention(RetentionPolicy.RUNTIME)
- public @interface Config {
- }
-
- /**
- * Stores internal states / methods associated with this config that's only needed in
- * junit-impl.
- */
- private RavenwoodConfig() {
- }
-
- /**
- * Return if the current process is running on a Ravenwood test environment.
- */
- public static boolean isOnRavenwood() {
- return RavenwoodRule.isOnRavenwood();
- }
-
- public static class Builder {
- private final RavenwoodConfig mConfig = new RavenwoodConfig();
-
- public Builder() {
- }
-
- /**
- * @deprecated no longer used. We always use an app UID.
- */
- @Deprecated
- public Builder setProcessSystem() {
- return this;
- }
-
- /**
- * @deprecated no longer used. We always use an app UID.
- */
- @Deprecated
- public Builder setProcessApp() {
- return this;
- }
-
- /**
- * @deprecated no longer used. Package name is set in the build file. (for now)
- */
- @Deprecated
- public Builder setPackageName(@NonNull String packageName) {
- return this;
- }
-
- /**
- * @deprecated no longer used. Package name is set in the build file. (for now)
- */
- @Deprecated
- public Builder setTargetPackageName(@NonNull String packageName) {
- return this;
- }
-
-
- /**
- * @deprecated no longer used. Target SDK level is set in the build file. (for now)
- */
- @Deprecated
- public Builder setTargetSdkLevel(int sdkLevel) {
- return this;
- }
-
- /**
- * @deprecated no longer used. Main thread is always available.
- */
- @Deprecated
- public Builder setProvideMainThread(boolean provideMainThread) {
- return this;
- }
-
- /**
- * @deprecated Use {@link RavenwoodRule.Builder#setSystemPropertyImmutable(String, Object)}
- */
- @Deprecated
- public Builder setSystemPropertyImmutable(@NonNull String key,
- @Nullable Object value) {
- return this;
- }
-
- /**
- * @deprecated Use {@link RavenwoodRule.Builder#setSystemPropertyMutable(String, Object)}
- */
- @Deprecated
- public Builder setSystemPropertyMutable(@NonNull String key,
- @Nullable Object value) {
- return this;
- }
-
- /**
- * @deprecated no longer used. All supported services are available.
- */
- @Deprecated
- public Builder setServicesRequired(@NonNull Class<?>... services) {
- return this;
- }
-
- public RavenwoodConfig build() {
- return mConfig;
- }
- }
-}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index d8cde0e..ffe5f6c 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -36,10 +36,8 @@
import java.util.regex.Pattern;
/**
- * @deprecated This class is undergoing a major change. Reach out to g/ravenwood if you need
- * any featues in it.
+ * Reach out to g/ravenwood if you need any features in it.
*/
-@Deprecated
public final class RavenwoodRule implements TestRule {
private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
diff --git a/ravenwood/runtime-jni/ravenwood_initializer.cpp b/ravenwood/runtime-jni/ravenwood_initializer.cpp
index dbbc345..391c5d5 100644
--- a/ravenwood/runtime-jni/ravenwood_initializer.cpp
+++ b/ravenwood/runtime-jni/ravenwood_initializer.cpp
@@ -140,7 +140,7 @@
if (gVM != nullptr && gRunnerState != nullptr) {
JNIEnv* env;
if (gVM->GetEnv((void**)&env, JNI_VERSION_1_4) >= 0) {
- ALOGI("%s access to system property '%s'", write ? "Write" : "Read", key);
+ ALOGV("%s access to system property '%s'", write ? "Write" : "Read", key);
env->CallStaticVoidMethod(gRunnerState, gCheckSystemPropertyAccess,
env->NewStringUTF(key), write ? JNI_TRUE : JNI_FALSE);
return;
@@ -208,7 +208,7 @@
};
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
- ALOGI("%s: JNI_OnLoad", __FILE__);
+ ALOGV("%s: JNI_OnLoad", __FILE__);
maybeRedirectLog();
diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp
index bab4c7e..8d8ed71 100644
--- a/ravenwood/runtime-jni/ravenwood_runtime.cpp
+++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp
@@ -204,7 +204,7 @@
};
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
- ALOGI("%s: JNI_OnLoad", __FILE__);
+ ALOGV("%s: JNI_OnLoad", __FILE__);
JNIEnv* env = GetJNIEnvOrDie(vm);
g_StructStat = FindGlobalClassOrDie(env, "android/system/StructStat");
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodConfigTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodConfigTest.java
deleted file mode 100644
index 306c2b39..0000000
--- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodConfigTest.java
+++ /dev/null
@@ -1,42 +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.ravenwoodtest.bivalenttest;
-
-import static android.platform.test.ravenwood.RavenwoodConfig.isOnRavenwood;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assume.assumeTrue;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test to make sure the config field is used.
- */
-@RunWith(AndroidJUnit4.class)
-public class RavenwoodConfigTest {
- private static final String PACKAGE_NAME = "com.android.ravenwoodtest.bivalenttest";
-
- @Test
- public void testConfig() {
- assumeTrue(isOnRavenwood());
- assertEquals(PACKAGE_NAME,
- InstrumentationRegistry.getInstrumentation().getContext().getPackageName());
- }
-}
diff --git a/services/Android.bp b/services/Android.bp
index fc0bb33..a7cb9bb 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -210,6 +210,35 @@
},
}
+soong_config_module_type {
+ name: "ondeviceintelligence_module_java_defaults",
+ module_type: "java_defaults",
+ config_namespace: "ANDROID",
+ bool_variables: [
+ "release_ondevice_intelligence_module",
+ "release_ondevice_intelligence_platform",
+ ],
+ properties: [
+ "libs",
+ "srcs",
+ "static_libs",
+ ],
+}
+
+// Conditionally add ondeviceintelligence stubs library
+ondeviceintelligence_module_java_defaults {
+ name: "ondeviceintelligence_conditionally",
+ soong_config_variables: {
+ release_ondevice_intelligence_module: {
+ libs: ["service-ondeviceintelligence.stubs.system_server"],
+ },
+ release_ondevice_intelligence_platform: {
+ srcs: [":service-ondeviceintelligence-sources"],
+ static_libs: ["modules-utils-backgroundthread"],
+ },
+ },
+}
+
// merge all required services into one jar
// ============================================================
soong_config_module_type {
@@ -236,6 +265,7 @@
"services_java_defaults",
"art_profile_java_defaults",
"services_crashrecovery_stubs_conditionally",
+ "ondeviceintelligence_conditionally",
],
installable: true,
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index dbfafbd..5c1ad74 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1070,8 +1070,11 @@
newValue, restoredFromSdk);
}
}
+ // Currently in SUW, the user can't see gesture shortcut option as the
+ // navigation system is set to button navigation. We'll rely on the
+ // SettingsBackupAgent to restore the settings since we don't
+ // need to merge an empty gesture target.
case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
- Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS,
Settings.Secure.ACCESSIBILITY_QS_TARGETS,
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE ->
restoreShortcutTargets(newValue,
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index f13e229..c17c340 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -24,12 +24,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
+import android.app.appfunctions.AppFunctionException;
import android.app.appfunctions.AppFunctionManager;
import android.app.appfunctions.AppFunctionManagerHelper;
import android.app.appfunctions.AppFunctionRuntimeMetadata;
import android.app.appfunctions.AppFunctionStaticMetadataHelper;
import android.app.appfunctions.ExecuteAppFunctionAidlRequest;
-import android.app.appfunctions.AppFunctionException;
import android.app.appfunctions.IAppFunctionEnabledCallback;
import android.app.appfunctions.IAppFunctionManager;
import android.app.appfunctions.IAppFunctionService;
@@ -158,8 +158,7 @@
} catch (SecurityException exception) {
safeExecuteAppFunctionCallback.onError(
new AppFunctionException(
- AppFunctionException.ERROR_DENIED,
- exception.getMessage()));
+ AppFunctionException.ERROR_DENIED, exception.getMessage()));
return null;
}
@@ -195,12 +194,12 @@
@NonNull SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback,
@NonNull IBinder callerBinder) {
UserHandle targetUser = requestInternal.getUserHandle();
- // TODO(b/354956319): Add and honor the new enterprise policies.
- if (mCallerValidator.isUserOrganizationManaged(targetUser)) {
+ UserHandle callingUser = UserHandle.getUserHandleForUid(callingUid);
+ if (!mCallerValidator.verifyEnterprisePolicyIsAllowed(callingUser, targetUser)) {
safeExecuteAppFunctionCallback.onError(
- new AppFunctionException(AppFunctionException.ERROR_SYSTEM_ERROR,
- "Cannot run on a device with a device owner or from the managed"
- + " profile."));
+ new AppFunctionException(
+ AppFunctionException.ERROR_ENTERPRISE_POLICY_DISALLOWED,
+ "Cannot run on a user with a restricted enterprise policy"));
return;
}
@@ -442,7 +441,8 @@
if (!bindServiceResult) {
Slog.e(TAG, "Failed to bind to the AppFunctionService");
safeExecuteAppFunctionCallback.onError(
- new AppFunctionException(AppFunctionException.ERROR_SYSTEM_ERROR,
+ new AppFunctionException(
+ AppFunctionException.ERROR_SYSTEM_ERROR,
"Failed to bind the AppFunctionService."));
}
}
@@ -495,8 +495,7 @@
return;
}
FutureGlobalSearchSession futureGlobalSearchSession =
- new FutureGlobalSearchSession(
- perUserAppSearchManager, AppFunctionExecutors.THREAD_POOL_EXECUTOR);
+ new FutureGlobalSearchSession(perUserAppSearchManager, THREAD_POOL_EXECUTOR);
AppFunctionMetadataObserver appFunctionMetadataObserver =
new AppFunctionMetadataObserver(
user.getUserHandle(),
diff --git a/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java b/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
index 5393b93..6191767 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
@@ -81,10 +81,12 @@
@NonNull String functionId);
/**
- * Checks if the user is organization managed.
+ * Checks if the app function policy is allowed.
*
+ * @param callingUser The current calling user.
* @param targetUser The user which the caller is requesting to execute as.
- * @return Whether the user is organization managed.
+ * @return Whether the app function policy is allowed.
*/
- boolean isUserOrganizationManaged(@NonNull UserHandle targetUser);
+ boolean verifyEnterprisePolicyIsAllowed(
+ @NonNull UserHandle callingUser, @NonNull UserHandle targetUser);
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java b/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
index e85a70d..69481c3 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
@@ -28,6 +28,7 @@
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.AppFunctionsPolicy;
import android.app.appsearch.AppSearchBatchResult;
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchManager.SearchContext;
@@ -39,7 +40,6 @@
import android.os.Binder;
import android.os.Process;
import android.os.UserHandle;
-import android.os.UserManager;
import com.android.internal.infra.AndroidFuture;
@@ -124,8 +124,7 @@
FutureAppSearchSession futureAppSearchSession =
new FutureAppSearchSessionImpl(
Objects.requireNonNull(
- mContext
- .createContextAsUser(targetUser, 0)
+ mContext.createContextAsUser(targetUser, 0)
.getSystemService(AppSearchManager.class)),
THREAD_POOL_EXECUTOR,
new SearchContext.Builder(APP_FUNCTION_STATIC_METADATA_DB).build());
@@ -168,13 +167,16 @@
}
@Override
- public boolean isUserOrganizationManaged(@NonNull UserHandle targetUser) {
- if (Objects.requireNonNull(mContext.getSystemService(DevicePolicyManager.class))
- .isDeviceManaged()) {
- return true;
- }
- return Objects.requireNonNull(mContext.getSystemService(UserManager.class))
- .isManagedProfile(targetUser.getIdentifier());
+ public boolean verifyEnterprisePolicyIsAllowed(
+ @NonNull UserHandle callingUser, @NonNull UserHandle targetUser) {
+ @AppFunctionsPolicy
+ int callingUserPolicy = getDevicePolicyManagerAsUser(callingUser).getAppFunctionsPolicy();
+ @AppFunctionsPolicy
+ int targetUserPolicy = getDevicePolicyManagerAsUser(targetUser).getAppFunctionsPolicy();
+ boolean isSameUser = callingUser.equals(targetUser);
+
+ return isAppFunctionPolicyAllowed(targetUserPolicy, isSameUser)
+ && isAppFunctionPolicyAllowed(callingUserPolicy, isSameUser);
}
/**
@@ -264,4 +266,18 @@
return Process.INVALID_UID;
}
}
+
+ private boolean isAppFunctionPolicyAllowed(
+ @AppFunctionsPolicy int userPolicy, boolean isSameUser) {
+ return switch (userPolicy) {
+ case DevicePolicyManager.APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY -> true;
+ case DevicePolicyManager.APP_FUNCTIONS_DISABLED_CROSS_PROFILE -> isSameUser;
+ default -> false;
+ };
+ }
+
+ private DevicePolicyManager getDevicePolicyManagerAsUser(@NonNull UserHandle targetUser) {
+ return mContext.createContextAsUser(targetUser, /* flags= */ 0)
+ .getSystemService(DevicePolicyManager.class);
+ }
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 762665c..cffdfbd 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -330,6 +330,8 @@
// Handler to the background thread that saves states to disk.
private Handler mSaveStateHandler;
+
+ private Handler mAlarmHandler;
// Handler to the background thread that saves generated previews to disk. All operations that
// modify saved previews must be run on this Handler.
private Handler mSavePreviewsHandler;
@@ -373,6 +375,7 @@
if (removeAppWidgetServiceIoFromCriticalPath()) {
mSaveStateHandler = new Handler(BackgroundThread.get().getLooper(),
this::handleSaveMessage);
+ mAlarmHandler = new Handler(BackgroundThread.get().getLooper());
} else {
mSaveStateHandler = BackgroundThread.getHandler();
}
@@ -2739,10 +2742,15 @@
}
if (provider.broadcast != null) {
final PendingIntent broadcast = provider.broadcast;
- mSaveStateHandler.post(() -> {
- mAlarmManager.cancel(broadcast);
- broadcast.cancel();
- });
+ Runnable cancelRunnable = () -> {
+ mAlarmManager.cancel(broadcast);
+ broadcast.cancel();
+ };
+ if (removeAppWidgetServiceIoFromCriticalPath()) {
+ mAlarmHandler.post(cancelRunnable);
+ } else {
+ mSaveStateHandler.post(cancelRunnable);
+ }
provider.broadcast = null;
}
}
@@ -3422,10 +3430,16 @@
// invariant and established the PendingIntent safely.
final long period = Math.max(info.updatePeriodMillis, MIN_UPDATE_PERIOD);
final PendingIntent broadcast = provider.broadcast;
- mSaveStateHandler.post(() ->
+
+ Runnable repeatRunnable = () -> {
mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + period, period, broadcast)
- );
+ SystemClock.elapsedRealtime() + period, period, broadcast);
+ };
+ if (removeAppWidgetServiceIoFromCriticalPath()) {
+ mAlarmHandler.post(repeatRunnable);
+ } else {
+ mSaveStateHandler.post(repeatRunnable);
+ }
}
}
}
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index 5e1b147..9c83757 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -9,6 +9,16 @@
}
flag {
+ name: "improve_fill_dialog_aconfig"
+ namespace: "autofill"
+ description: "Improvements for Fill Dialog. Guard DeviceConfig rollout "
+ bug: "382493181"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "fill_fields_from_current_session_only"
namespace: "autofill"
description: "Only fill autofill fields that are part of the current session."
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
index 219b788..5e7e557 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
@@ -354,6 +354,13 @@
}
}
+ private void handleOnInputMethodStartInputView() {
+ synchronized (mLock) {
+ mUiCallback.onInputMethodStartInputView();
+ handleOnReceiveImeStatusUpdated(true, true);
+ }
+ }
+
/**
* Handles the IME session status received from the IME.
*
@@ -437,8 +444,8 @@
final AutofillInlineSuggestionsRequestSession session = mSession.get();
if (session != null) {
session.mHandler.sendMessage(obtainMessage(
- AutofillInlineSuggestionsRequestSession::handleOnReceiveImeStatusUpdated,
- session, true, true));
+ AutofillInlineSuggestionsRequestSession::handleOnInputMethodStartInputView,
+ session));
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 259ea14..cba8c66 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -2139,6 +2139,32 @@
}
@Override
+ public void notifyImeAnimationStart(int sessionId, long startTimeMs, int userId) {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
+ if (service != null) {
+ service.notifyImeAnimationStart(sessionId, startTimeMs, getCallingUid());
+ } else if (sVerbose) {
+ Slog.v(TAG, "notifyImeAnimationStart(): no service for " + userId);
+ }
+ }
+ }
+
+ @Override
+ public void notifyImeAnimationEnd(int sessionId, long endTimeMs, int userId) {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
+ if (service != null) {
+ service.notifyImeAnimationEnd(sessionId, endTimeMs, getCallingUid());
+ } else if (sVerbose) {
+ Slog.v(TAG, "notifyImeAnimationEnd(): no service for " + userId);
+ }
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 5cf96bf..0fa43ac 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -823,6 +823,36 @@
}
@GuardedBy("mLock")
+ public void notifyImeAnimationStart(int sessionId, long startTimeMs, int uid) {
+ if (!isEnabledLocked()) {
+ Slog.wtf(TAG, "Service not enabled");
+ return;
+ }
+ final Session session = mSessions.get(sessionId);
+ if (session == null || uid != session.uid) {
+ Slog.v(TAG, "notifyImeAnimationStart(): no session for "
+ + sessionId + "(" + uid + ")");
+ return;
+ }
+ session.notifyImeAnimationStart(startTimeMs);
+ }
+
+ @GuardedBy("mLock")
+ public void notifyImeAnimationEnd(int sessionId, long endTimeMs, int uid) {
+ if (!isEnabledLocked()) {
+ Slog.wtf(TAG, "Service not enabled");
+ return;
+ }
+ final Session session = mSessions.get(sessionId);
+ if (session == null || uid != session.uid) {
+ Slog.v(TAG, "notifyImeAnimationEnd(): no session for "
+ + sessionId + "(" + uid + ")");
+ return;
+ }
+ session.notifyImeAnimationEnd(endTimeMs);
+ }
+
+ @GuardedBy("mLock")
@Override // from PerUserSystemService
protected void handlePackageUpdateLocked(@NonNull String packageName) {
final ServiceInfo serviceInfo = mFieldClassificationStrategy.getServiceInfo();
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 6b227d7..9c6e4741 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -40,6 +40,7 @@
import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
import static android.service.autofill.Flags.highlightAutofillSingleField;
+import static android.service.autofill.Flags.improveFillDialogAconfig;
import static android.view.autofill.AutofillManager.ACTION_RESPONSE_EXPIRED;
import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
@@ -50,6 +51,7 @@
import static android.view.autofill.AutofillManager.EXTRA_AUTOFILL_REQUEST_ID;
import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
+
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_EXPLICITLY_REQUESTED;
import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_NORMAL_TRIGGER;
@@ -184,6 +186,7 @@
import android.view.autofill.IAutofillWindowPresenter;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.widget.RemoteViews;
+
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
@@ -195,6 +198,7 @@
import com.android.server.autofill.ui.PendingUi;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
+
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -248,6 +252,8 @@
private static final int DEFAULT__FILL_REQUEST_ID_SNAPSHOT = -2;
private static final int DEFAULT__FIELD_CLASSIFICATION_REQUEST_ID_SNAPSHOT = -2;
+ private static final long DEFAULT_UNASSIGNED_TIME = -1;
+
static final String SESSION_ID_KEY = "autofill_session_id";
static final String REQUEST_ID_KEY = "autofill_request_id";
@@ -292,6 +298,31 @@
@Retention(RetentionPolicy.SOURCE)
@interface SessionState {}
+ /**
+ * Indicates fill dialog will not be shown.
+ */
+ private static final int SHOW_FILL_DIALOG_NO = 0;
+
+ /**
+ * Indicates fill dialog is shown.
+ */
+ private static final int SHOW_FILL_DIALOG_YES = 1;
+
+ /**
+ * Indicates fill dialog can be shown, but we need to wait.
+ * For eg, if the IME animation is happening, we need for it to complete before fill dialog can
+ * be shown.
+ */
+ private static final int SHOW_FILL_DIALOG_WAIT = 2;
+
+ @IntDef(prefix = { "SHOW_FILL_DIALOG_" }, value = {
+ SHOW_FILL_DIALOG_NO,
+ SHOW_FILL_DIALOG_YES,
+ SHOW_FILL_DIALOG_WAIT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface ShowFillDialogState{}
+
@GuardedBy("mLock")
private final SessionFlags mSessionFlags;
@@ -576,6 +607,47 @@
private boolean mIgnoreViewStateResetToEmpty;
+ /**
+ * Whether improveFillDialog feature is enabled or not.
+ * Configured via device config flag and aconfig flag.
+ */
+ private final boolean mImproveFillDialogEnabled;
+
+ /**
+ * Timeout, after which, fill dialog won't be shown.
+ * Configured via device config flag.
+ */
+ private final long mFillDialogTimeoutMs;
+
+ /**
+ * Time to wait after ime Animation ends before showing up fill dialog.
+ * Configured via device config flag.
+ */
+ private final long mFillDialogMinWaitAfterImeAnimationMs;
+
+ /**
+ * Indicates the need to wait for ime animation to end before showing up fill dialog.
+ * This is set when we receive notification of ime animation start.
+ * Focussing on one input field from another wouldn't cause ime to re-animate. So this will let
+ * us know whether we need to wait for ime animation finish notification.
+ */
+ private boolean mWaitForImeAnimation;
+
+ /**
+ * A runnable set to run when there is a need to wait for ime animation to end before showing
+ * up fill dialog. This is set only if the fill response has been received, and the response
+ * is eligible for showing up fill dialog, but the ime animation hasn't completed. This allows
+ * for this runnable to be scheduled/run when the ime animation ends, in order to show fill
+ * dialog.
+ */
+ private Runnable mFillDialogRunnable;
+
+ private long mImeAnimationFinishTimeMs = DEFAULT_UNASSIGNED_TIME;
+
+ private long mImeAnimationStartTimeMs = DEFAULT_UNASSIGNED_TIME;
+
+ private long mLastInputStartTime = DEFAULT_UNASSIGNED_TIME;
+
/*
* Id of the previous view that was entered. Once set, it would only be replaced by non-null
* view ids.
@@ -1493,6 +1565,12 @@
// Now request the assist structure data.
requestAssistStructureLocked(requestId, flags);
+ if (mImproveFillDialogEnabled) {
+ // New request has been sent, so re-enable fill dialog.
+ // Fill dialog is eligible to be shown after each Fill request.
+ enableFillDialog();
+ }
+
return Optional.of(requestId);
}
@@ -1657,6 +1735,11 @@
mSaveEventLogger = SaveEventLogger.forSessionId(sessionId, mLatencyBaseTime);
mIsPrimaryCredential = isPrimaryCredential;
mIgnoreViewStateResetToEmpty = AutofillFeatureFlags.shouldIgnoreViewStateResetToEmpty();
+ mImproveFillDialogEnabled =
+ improveFillDialogAconfig() && AutofillFeatureFlags.isImproveFillDialogEnabled();
+ mFillDialogTimeoutMs = AutofillFeatureFlags.getFillDialogTimeoutMs();
+ mFillDialogMinWaitAfterImeAnimationMs =
+ AutofillFeatureFlags.getFillDialogMinWaitAfterImeAnimationtEndMs();
synchronized (mLock) {
mSessionFlags = new SessionFlags();
@@ -1682,6 +1765,13 @@
public void notifyInlineUiHidden(AutofillId autofillId) {
notifyFillUiHidden(autofillId);
}
+
+ @Override
+ public void onInputMethodStartInputView() {
+ // TODO(b/377868687): This method isn't called when IME doesn't
+ // support inline suggestion. Handle those cases as well.
+ onInputMethodStart();
+ }
});
mMetricsLogger.write(
@@ -3044,6 +3134,12 @@
}
}
+ private void onInputMethodStart() {
+ synchronized (mLock) {
+ mLastInputStartTime = SystemClock.elapsedRealtime();
+ }
+ }
+
private void doStartIntentSender(IntentSender intentSender, Intent intent) {
try {
synchronized (mLock) {
@@ -5407,6 +5503,15 @@
}
}
+ private void resetImeAnimationState() {
+ synchronized (mLock) {
+ mWaitForImeAnimation = false;
+ mImeAnimationStartTimeMs = DEFAULT_UNASSIGNED_TIME;
+ mImeAnimationFinishTimeMs = DEFAULT_UNASSIGNED_TIME;
+ mLastInputStartTime = DEFAULT_UNASSIGNED_TIME;
+ }
+ }
+
@Override
public void onFillReady(
@NonNull FillResponse response,
@@ -5452,18 +5557,24 @@
final AutofillId[] ids = response.getFillDialogTriggerIds();
if (ids != null && ArrayUtils.contains(ids, filledId)) {
- if (requestShowFillDialog(response, filledId, filterText, flags)) {
+ @ShowFillDialogState int fillDialogState =
+ requestShowFillDialog(response, filledId, filterText, flags);
+ if (fillDialogState == SHOW_FILL_DIALOG_YES) {
synchronized (mLock) {
final ViewState currentView = mViewStates.get(mCurrentViewId);
currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN);
}
- // Just show fill dialog once, so disabled after shown.
+ // Just show fill dialog once per fill request, so disabled after shown.
// Note: Cannot disable before requestShowFillDialog() because the method
- // need to check whether fill dialog enabled.
+ // need to check whether fill dialog is enabled.
setFillDialogDisabled();
+ resetImeAnimationState();
return;
- } else {
+ } else if (fillDialogState == SHOW_FILL_DIALOG_NO) {
+ resetImeAnimationState();
setFillDialogDisabled();
+ } else { // SHOW_FILL_DIALOG_WAIT
+ return;
}
}
@@ -5559,7 +5670,20 @@
}
}
+ private void enableFillDialog() {
+ if (sVerbose) {
+ Slog.v(TAG, "Enabling Fill Dialog....");
+ }
+ synchronized (mLock) {
+ mSessionFlags.mFillDialogDisabled = false;
+ }
+ notifyClientFillDialogTriggerIds(null);
+ }
+
private void setFillDialogDisabled() {
+ if (sVerbose) {
+ Slog.v(TAG, "Disabling Fill Dialog.");
+ }
synchronized (mLock) {
mSessionFlags.mFillDialogDisabled = true;
}
@@ -5577,24 +5701,28 @@
}
}
- private boolean requestShowFillDialog(
+ private @ShowFillDialogState int requestShowFillDialog(
FillResponse response, AutofillId filledId, String filterText, int flags) {
if (!isFillDialogUiEnabled()) {
+ // TODO(b/377868687): The above check includes credman fields. We may want to show
+ // credman fields again.
// Unsupported fill dialog UI
- if (sDebug) Log.w(TAG, "requestShowFillDialog: fill dialog is disabled");
- return false;
+ if (sDebug) Log.w(TAG, "requestShowFillDialog(): fill dialog is disabled");
+ return SHOW_FILL_DIALOG_NO;
}
- if ((flags & FillRequest.FLAG_IME_SHOWING) != 0) {
- // IME is showing, fallback to normal suggestions UI
- if (sDebug) Log.w(TAG, "requestShowFillDialog: IME is showing");
- return false;
- }
+ if (!mImproveFillDialogEnabled) {
+ if ((flags & FillRequest.FLAG_IME_SHOWING) != 0) {
+ // IME is showing, fallback to normal suggestions UI
+ if (sDebug) Log.w(TAG, "requestShowFillDialog(): IME is showing");
+ return SHOW_FILL_DIALOG_NO;
+ }
- if (mInlineSessionController.isImeShowing()) {
- // IME is showing, fallback to normal suggestions UI
- // Note: only work when inline suggestions supported
- return false;
+ if (mInlineSessionController.isImeShowing()) {
+ // IME is showing, fallback to normal suggestions UI
+ // Note: only work when inline suggestions supported
+ return SHOW_FILL_DIALOG_NO;
+ }
}
synchronized (mLock) {
@@ -5602,29 +5730,84 @@
|| !ArrayUtils.contains(mLastFillDialogTriggerIds, filledId)) {
// Last fill dialog triggered ids are changed.
if (sDebug) Log.w(TAG, "Last fill dialog triggered ids are changed.");
- return false;
+ return SHOW_FILL_DIALOG_NO;
+ }
+
+ if (mImproveFillDialogEnabled && mInlineSessionController.isImeShowing()) {
+ long currentTimestampMs = SystemClock.elapsedRealtime();
+ long durationMs = currentTimestampMs - mLastInputStartTime;
+ if (sVerbose) {
+ Log.d(TAG, "IME is showing. Checking for elapsed time ");
+ Log.d(TAG, "IME is showing. Timestamps start: " + mLastInputStartTime
+ + " current: " + currentTimestampMs + " duration: " + durationMs
+ + " mFillDialogTimeoutMs: " + mFillDialogTimeoutMs);
+ }
+
+ // Following situations can arise wrt IME animation.
+ // 1. No animation happening (eg IME already animated). In that case,
+ // mWaitForImeAnimation should be false. This is possible if the IME is already up
+ // on a field, but the user focusses on another field. Under such condition,
+ // since IME has already animated, there won't be another animation. However,
+ // onInputStartInputView is still called.
+ // 2. Animation is still proceeding. We should wait for animation to finish,
+ // and then proceed.
+ // 3. Animation is complete.
+ if (mWaitForImeAnimation) {
+ // we need to wait for animation to happen. We can't return from here yet.
+ // This is the situation #2 described above.
+ Log.d(TAG, "Waiting for ime animation to complete before showing fill dialog");
+ mFillDialogRunnable = createFillDialogEvalRunnable(
+ response, filledId, filterText, flags);
+ return SHOW_FILL_DIALOG_WAIT;
+ }
+
+ // Incorporate situations 1 & 3 discussed above. We calculate the duration from the
+ // max of start input time or the ime finish time
+ long effectiveDuration = currentTimestampMs
+ - Math.max(mLastInputStartTime, mImeAnimationFinishTimeMs);
+ if (effectiveDuration >= mFillDialogTimeoutMs) {
+ Log.d(TAG, "Fill dialog not shown since IME has been up for more time than "
+ + mFillDialogTimeoutMs + "ms");
+ return SHOW_FILL_DIALOG_NO;
+ } else if (effectiveDuration < mFillDialogMinWaitAfterImeAnimationMs) {
+ // we need to wait for some time after animation ends
+ Runnable runnable = createFillDialogEvalRunnable(
+ response, filledId, filterText, flags);
+ mHandler.postDelayed(runnable,
+ mFillDialogMinWaitAfterImeAnimationMs - effectiveDuration);
+ return SHOW_FILL_DIALOG_WAIT;
+ }
}
}
+ showFillDialog(response, filledId, filterText);
+ return SHOW_FILL_DIALOG_YES;
+ }
+
+ private Runnable createFillDialogEvalRunnable(
+ @NonNull FillResponse response,
+ @NonNull AutofillId filledId,
+ String filterText,
+ int flags) {
+ return () -> {
+ synchronized (mLock) {
+ AutofillValue value = AutofillValue.forText(filterText);
+ onFillReady(response, filledId, value, flags);
+ }
+ };
+ }
+
+ private void showFillDialog(FillResponse response, AutofillId filledId, String filterText) {
Drawable serviceIcon = null;
+ PresentationStatsEventLogger logger = null;
synchronized (mLock) {
serviceIcon = getServiceIcon(response);
+ logger = mPresentationStatsEventLogger;
}
- getUiForShowing()
- .showFillDialog(
- filledId,
- response,
- filterText,
- mService.getServicePackageName(),
- mComponentName,
- serviceIcon,
- this,
- id,
- mCompatMode,
- mPresentationStatsEventLogger,
- mLock);
- return true;
+ getUiForShowing().showFillDialog(filledId, response, filterText,
+ mService.getServicePackageName(), mComponentName, serviceIcon, this,
+ id, mCompatMode, logger, mLock);
}
/**
@@ -7689,6 +7872,30 @@
mSessionState = STATE_REMOVED;
}
+ @GuardedBy("mLock")
+ public void notifyImeAnimationStart(long startTimeMs) {
+ mImeAnimationStartTimeMs = startTimeMs;
+ mWaitForImeAnimation = true;
+ }
+
+ @GuardedBy("mLock")
+ public void notifyImeAnimationEnd(long endTimeMs) {
+ mImeAnimationFinishTimeMs = endTimeMs;
+ // Make sure to use mRunnable with synchronized
+ if (mFillDialogRunnable != null) {
+ if (sVerbose) {
+ Log.v(TAG, "Ime animation ended, starting fill dialog.");
+ }
+ mHandler.postDelayed(mFillDialogRunnable, mFillDialogMinWaitAfterImeAnimationMs);
+ mFillDialogRunnable = null;
+ } else {
+ if (sVerbose) {
+ Log.v(TAG, "Ime animation ended, no runnable present.");
+ }
+ }
+ mWaitForImeAnimation = false;
+ }
+
void onPendingSaveUi(int operation, @NonNull IBinder token) {
getUiForShowing().onPendingSaveUi(operation, token);
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
index ffc80ee..7287bdd 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
@@ -409,5 +409,10 @@
* Callback to notify inline ui is hidden.
*/
void notifyInlineUiHidden(@NonNull AutofillId autofillId);
+
+ /**
+ * Callback to notify input method started.
+ */
+ void onInputMethodStartInputView();
}
}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index a90b693..3025e2e 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -314,8 +314,6 @@
private static final String SERIAL_ID_FILE = "serial_id";
- private static final String SKIP_USER_FACING_PACKAGES = "backup_skip_user_facing_packages";
-
private final @UserIdInt int mUserId;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
private final TransportManager mTransportManager;
@@ -3503,40 +3501,6 @@
}
}
- /**
- * We want to skip backup/restore of certain packages if 'backup_skip_user_facing_packages' is
- * set to true in secure settings. See b/153940088 for details.
- *
- * TODO(b/154822946): Remove this logic in the next release.
- */
- public List<PackageInfo> filterUserFacingPackages(List<PackageInfo> packages) {
- if (!shouldSkipUserFacingData()) {
- return packages;
- }
-
- List<PackageInfo> filteredPackages = new ArrayList<>(packages.size());
- for (PackageInfo packageInfo : packages) {
- if (!shouldSkipPackage(packageInfo.packageName)) {
- filteredPackages.add(packageInfo);
- } else {
- Slog.i(TAG, "Will skip backup/restore for " + packageInfo.packageName);
- }
- }
-
- return filteredPackages;
- }
-
- @VisibleForTesting
- public boolean shouldSkipUserFacingData() {
- return Settings.Secure.getInt(mContext.getContentResolver(), SKIP_USER_FACING_PACKAGES,
- /* def */ 0) != 0;
- }
-
- @VisibleForTesting
- public boolean shouldSkipPackage(String packageName) {
- return WALLPAPER_PACKAGE.equals(packageName);
- }
-
private void updateStateForTransport(String newTransportName) {
// Publish the name change
Settings.Secure.putStringForUser(mContext.getContentResolver(),
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 7994948..990c941 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -272,8 +272,6 @@
}
}
- mPackages = backupManagerService.filterUserFacingPackages(mPackages);
-
Set<String> packageNames = Sets.newHashSet();
for (PackageInfo pkgInfo : mPackages) {
packageNames.add(pkgInfo.packageName);
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index dad84c8..ec9d340 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -315,8 +315,6 @@
}
}
- mAcceptSet = backupManagerService.filterUserFacingPackages(mAcceptSet);
-
if (MORE_DEBUG) {
Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size());
for (PackageInfo info : mAcceptSet) {
diff --git a/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java b/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java
index 4bcfb33..7c8604f 100644
--- a/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java
+++ b/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java
@@ -240,9 +240,9 @@
boolean matchesMacAddress = Objects.equals(
associationInfo.getDeviceMacAddress(),
restored.getDeviceMacAddress());
- boolean matchesDeviceId = !Flags.associationTag()
- || (associationInfo.getDeviceId() != null
- && associationInfo.getDeviceId().isSameDevice(restored.getDeviceId()));
+ boolean matchesDeviceId = Flags.associationTag()
+ && (associationInfo.getDeviceId() != null
+ && associationInfo.getDeviceId().isSameDevice(restored.getDeviceId()));
return matchesMacAddress || matchesDeviceId;
};
AssociationInfo local = CollectionUtils.find(localAssociations, isSameDevice);
diff --git a/services/core/Android.bp b/services/core/Android.bp
index ffa259b..b3d85f8 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -127,6 +127,7 @@
"android.hardware.power-java_shared",
"latest_android_hardware_broadcastradio_java_static",
"services_crashrecovery_stubs_conditionally",
+ "ondeviceintelligence_conditionally",
],
srcs: [
":android.hardware.tv.hdmi.connection-V1-java-source",
@@ -157,8 +158,8 @@
"android.hardware.light-V2.0-java",
"android.hardware.gnss-V2-java",
"android.hardware.vibrator-V3-java",
+ "androidx.annotation_annotation",
"app-compat-annotations",
- "art_exported_aconfig_flags_lib",
"framework-tethering.stubs.module_lib",
"keepanno-annotations",
"service-art.stubs.system_server",
@@ -245,6 +246,7 @@
"locksettings_flags_lib",
"profiling_flags_lib",
"android.adpf.sessionmanager_aidl-java",
+ "uprobestats_flags_java_lib",
],
javac_shard_size: 50,
javacflags: [
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index ccc44a4..d84a892 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -16,9 +16,14 @@
package com.android.server;
+import static android.service.quickaccesswallet.Flags.launchWalletOptionOnPowerDoubleTap;
+
+import static com.android.hardware.input.Flags.overridePowerKeyBehaviorInFocusedWindow;
import static com.android.internal.R.integer.config_defaultMinEmergencyGestureTapDurationMillis;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -33,6 +38,7 @@
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
+import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
@@ -41,10 +47,12 @@
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
+import android.service.quickaccesswallet.QuickAccessWalletClient;
import android.util.MutableBoolean;
import android.util.Slog;
import android.view.KeyEvent;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
@@ -70,7 +78,8 @@
* Time in milliseconds in which the power button must be pressed twice so it will be considered
* as a camera launch.
*/
- @VisibleForTesting static final long CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS = 300;
+ @VisibleForTesting
+ static final long POWER_DOUBLE_TAP_MAX_TIME_MS = 300;
/**
@@ -100,10 +109,23 @@
@VisibleForTesting
static final int EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX = 5000;
- /**
- * Number of taps required to launch camera shortcut.
- */
- private static final int CAMERA_POWER_TAP_COUNT_THRESHOLD = 2;
+ /** Indicates camera should be launched on power double tap. */
+ @VisibleForTesting static final int LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER = 0;
+
+ /** Indicates wallet should be launched on power double tap. */
+ @VisibleForTesting static final int LAUNCH_WALLET_ON_DOUBLE_TAP_POWER = 1;
+
+ /** Number of taps required to launch the double tap shortcut (either camera or wallet). */
+ public static final int DOUBLE_POWER_TAP_COUNT_THRESHOLD = 2;
+
+ /** Bundle to send with PendingIntent to grant background activity start privileges. */
+ private static final Bundle GRANT_BACKGROUND_START_PRIVILEGES =
+ ActivityOptions.makeBasic()
+ .setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS)
+ .toBundle();
+
+ private final QuickAccessWalletClient mQuickAccessWalletClient;
/** The listener that receives the gesture event. */
private final GestureEventListener mGestureListener = new GestureEventListener();
@@ -158,6 +180,9 @@
*/
private boolean mCameraDoubleTapPowerEnabled;
+ /** Whether wallet double tap power button gesture is currently enabled. */
+ private boolean mWalletDoubleTapPowerEnabled;
+
/**
* Whether emergency gesture is currently enabled
*/
@@ -204,14 +229,22 @@
}
}
public GestureLauncherService(Context context) {
- this(context, new MetricsLogger(), new UiEventLoggerImpl());
+ this(
+ context,
+ new MetricsLogger(),
+ QuickAccessWalletClient.create(context),
+ new UiEventLoggerImpl());
}
@VisibleForTesting
- GestureLauncherService(Context context, MetricsLogger metricsLogger,
+ public GestureLauncherService(
+ Context context,
+ MetricsLogger metricsLogger,
+ QuickAccessWalletClient quickAccessWalletClient,
UiEventLogger uiEventLogger) {
super(context);
mContext = context;
+ mQuickAccessWalletClient = quickAccessWalletClient;
mMetricsLogger = metricsLogger;
mUiEventLogger = uiEventLogger;
}
@@ -237,6 +270,9 @@
"GestureLauncherService");
updateCameraRegistered();
updateCameraDoubleTapPowerEnabled();
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ updateWalletDoubleTapPowerEnabled();
+ }
updateEmergencyGestureEnabled();
updateEmergencyGesturePowerButtonCooldownPeriodMs();
@@ -292,6 +328,14 @@
}
@VisibleForTesting
+ void updateWalletDoubleTapPowerEnabled() {
+ boolean enabled = isWalletDoubleTapPowerSettingEnabled(mContext, mUserId);
+ synchronized (this) {
+ mWalletDoubleTapPowerEnabled = enabled;
+ }
+ }
+
+ @VisibleForTesting
void updateEmergencyGestureEnabled() {
boolean enabled = isEmergencyGestureSettingEnabled(mContext, mUserId);
synchronized (this) {
@@ -418,10 +462,33 @@
Settings.Secure.CAMERA_GESTURE_DISABLED, 0, userId) == 0);
}
+ /** Checks if camera should be launched on double press of the power button. */
public static boolean isCameraDoubleTapPowerSettingEnabled(Context context, int userId) {
- return isCameraDoubleTapPowerEnabled(context.getResources())
- && (Settings.Secure.getIntForUser(context.getContentResolver(),
- Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, 0, userId) == 0);
+ boolean res;
+
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ res = isDoubleTapPowerGestureSettingEnabled(context, userId)
+ && getDoubleTapPowerGestureAction(context, userId)
+ == LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER;
+ } else {
+ // These are legacy settings that will be deprecated once the option to launch both
+ // wallet and camera has been created.
+ res = isCameraDoubleTapPowerEnabled(context.getResources())
+ && (Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, 0, userId) == 0);
+ }
+ return res;
+ }
+
+ /** Checks if wallet should be launched on double tap of the power button. */
+ public static boolean isWalletDoubleTapPowerSettingEnabled(Context context, int userId) {
+ if (!launchWalletOptionOnPowerDoubleTap()) {
+ return false;
+ }
+
+ return isDoubleTapPowerGestureSettingEnabled(context, userId)
+ && getDoubleTapPowerGestureAction(context, userId)
+ == LAUNCH_WALLET_ON_DOUBLE_TAP_POWER;
}
public static boolean isCameraLiftTriggerSettingEnabled(Context context, int userId) {
@@ -441,6 +508,28 @@
isDefaultEmergencyGestureEnabled(context.getResources()) ? 1 : 0, userId) != 0;
}
+ private static int getDoubleTapPowerGestureAction(Context context, int userId) {
+ return Settings.Secure.getIntForUser(
+ context.getContentResolver(),
+ Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE,
+ LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER,
+ userId);
+ }
+
+ /** Whether the shortcut to launch app on power double press is enabled. */
+ private static boolean isDoubleTapPowerGestureSettingEnabled(Context context, int userId) {
+ return Settings.Secure.getIntForUser(
+ context.getContentResolver(),
+ Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED,
+ isDoubleTapConfigEnabled(context.getResources()) ? 1 : 0,
+ userId)
+ == 1;
+ }
+
+ private static boolean isDoubleTapConfigEnabled(Resources resources) {
+ return resources.getBoolean(R.bool.config_doubleTapPowerGestureEnabled);
+ }
+
/**
* Gets power button cooldown period in milliseconds after emergency gesture is triggered. The
* value is capped at a maximum
@@ -494,10 +583,56 @@
* Whether GestureLauncherService should be enabled according to system properties.
*/
public static boolean isGestureLauncherEnabled(Resources resources) {
- return isCameraLaunchEnabled(resources)
- || isCameraDoubleTapPowerEnabled(resources)
- || isCameraLiftTriggerEnabled(resources)
- || isEmergencyGestureEnabled(resources);
+ boolean res =
+ isCameraLaunchEnabled(resources)
+ || isCameraLiftTriggerEnabled(resources)
+ || isEmergencyGestureEnabled(resources);
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ res |= isDoubleTapConfigEnabled(resources);
+ } else {
+ res |= isCameraDoubleTapPowerEnabled(resources);
+ }
+ return res;
+ }
+
+ /**
+ * Processes a power key event in GestureLauncherService without performing an action. This
+ * method is called on every KEYCODE_POWER ACTION_DOWN event and ensures that, even if
+ * KEYCODE_POWER events are passed to and handled by the app, the GestureLauncherService still
+ * keeps track of all running KEYCODE_POWER events for its gesture detection and relevant
+ * actions.
+ */
+ public void processPowerKeyDown(KeyEvent event) {
+ if (mEmergencyGestureEnabled && mEmergencyGesturePowerButtonCooldownPeriodMs >= 0
+ && event.getEventTime() - mLastEmergencyGestureTriggered
+ < mEmergencyGesturePowerButtonCooldownPeriodMs) {
+ return;
+ }
+ if (event.isLongPress()) {
+ return;
+ }
+
+ final long powerTapInterval;
+
+ synchronized (this) {
+ powerTapInterval = event.getEventTime() - mLastPowerDown;
+ mLastPowerDown = event.getEventTime();
+ if (powerTapInterval >= POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS) {
+ // Tap too slow, reset consecutive tap counts.
+ mFirstPowerDown = event.getEventTime();
+ mPowerButtonConsecutiveTaps = 1;
+ mPowerButtonSlowConsecutiveTaps = 1;
+ } else if (powerTapInterval >= POWER_DOUBLE_TAP_MAX_TIME_MS) {
+ // Tap too slow for shortcuts
+ mFirstPowerDown = event.getEventTime();
+ mPowerButtonConsecutiveTaps = 1;
+ mPowerButtonSlowConsecutiveTaps++;
+ } else if (powerTapInterval > 0) {
+ // Fast consecutive tap
+ mPowerButtonConsecutiveTaps++;
+ mPowerButtonSlowConsecutiveTaps++;
+ }
+ }
}
/**
@@ -507,8 +642,8 @@
* @param outLaunched true if some action is taken as part of the key intercept (eg, app launch)
* @return true if the key down event is intercepted
*/
- public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive,
- MutableBoolean outLaunched) {
+ public boolean interceptPowerKeyDown(
+ KeyEvent event, boolean interactive, MutableBoolean outLaunched) {
if (mEmergencyGestureEnabled && mEmergencyGesturePowerButtonCooldownPeriodMs >= 0
&& event.getEventTime() - mLastEmergencyGestureTriggered
< mEmergencyGesturePowerButtonCooldownPeriodMs) {
@@ -530,6 +665,7 @@
return false;
}
boolean launchCamera = false;
+ boolean launchWallet = false;
boolean launchEmergencyGesture = false;
boolean intercept = false;
long powerTapInterval;
@@ -541,12 +677,12 @@
mFirstPowerDown = event.getEventTime();
mPowerButtonConsecutiveTaps = 1;
mPowerButtonSlowConsecutiveTaps = 1;
- } else if (powerTapInterval >= CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) {
+ } else if (powerTapInterval >= POWER_DOUBLE_TAP_MAX_TIME_MS) {
// Tap too slow for shortcuts
mFirstPowerDown = event.getEventTime();
mPowerButtonConsecutiveTaps = 1;
mPowerButtonSlowConsecutiveTaps++;
- } else {
+ } else if (!overridePowerKeyBehaviorInFocusedWindow() || powerTapInterval > 0) {
// Fast consecutive tap
mPowerButtonConsecutiveTaps++;
mPowerButtonSlowConsecutiveTaps++;
@@ -586,10 +722,16 @@
}
}
if (mCameraDoubleTapPowerEnabled
- && powerTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS
- && mPowerButtonConsecutiveTaps == CAMERA_POWER_TAP_COUNT_THRESHOLD) {
+ && powerTapInterval < POWER_DOUBLE_TAP_MAX_TIME_MS
+ && mPowerButtonConsecutiveTaps == DOUBLE_POWER_TAP_COUNT_THRESHOLD) {
launchCamera = true;
intercept = interactive;
+ } else if (launchWalletOptionOnPowerDoubleTap()
+ && mWalletDoubleTapPowerEnabled
+ && powerTapInterval < POWER_DOUBLE_TAP_MAX_TIME_MS
+ && mPowerButtonConsecutiveTaps == DOUBLE_POWER_TAP_COUNT_THRESHOLD) {
+ launchWallet = true;
+ intercept = interactive;
}
}
if (mPowerButtonConsecutiveTaps > 1 || mPowerButtonSlowConsecutiveTaps > 1) {
@@ -608,6 +750,10 @@
(int) powerTapInterval);
mUiEventLogger.log(GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER);
}
+ } else if (launchWallet) {
+ Slog.i(TAG, "Power button double tap gesture detected, launching wallet. Interval="
+ + powerTapInterval + "ms");
+ launchWallet = sendGestureTargetActivityPendingIntent();
} else if (launchEmergencyGesture) {
Slog.i(TAG, "Emergency gesture detected, launching.");
launchEmergencyGesture = handleEmergencyGesture();
@@ -623,11 +769,74 @@
mPowerButtonSlowConsecutiveTaps);
mMetricsLogger.histogram("power_double_tap_interval", (int) powerTapInterval);
- outLaunched.value = launchCamera || launchEmergencyGesture;
+ outLaunched.value = launchCamera || launchEmergencyGesture || launchWallet;
// Intercept power key event if the press is part of a gesture (camera, eGesture) and the
// user has completed setup.
return intercept && isUserSetupComplete();
}
+
+ /**
+ * Fetches and sends gestureTargetActivityPendingIntent from QuickAccessWallet, which is a
+ * specific activity that QuickAccessWalletService has defined to be launch on detection of the
+ * power button gesture.
+ */
+ private boolean sendGestureTargetActivityPendingIntent() {
+ boolean userSetupComplete = isUserSetupComplete();
+ if (mQuickAccessWalletClient == null
+ || !mQuickAccessWalletClient.isWalletServiceAvailable()) {
+ Slog.w(TAG, "QuickAccessWalletService is not available, ignoring wallet gesture.");
+ return false;
+ }
+
+ if (!userSetupComplete) {
+ if (DBG) {
+ Slog.d(TAG, "userSetupComplete = false, ignoring wallet gesture.");
+ }
+ return false;
+ }
+ if (DBG) {
+ Slog.d(TAG, "userSetupComplete = true, performing wallet gesture.");
+ }
+
+ mQuickAccessWalletClient.getGestureTargetActivityPendingIntent(
+ getContext().getMainExecutor(),
+ gesturePendingIntent -> {
+ if (gesturePendingIntent == null) {
+ Slog.d(TAG, "getGestureTargetActivityPendingIntent is null.");
+ sendFallbackPendingIntent();
+ return;
+ }
+ sendPendingIntentWithBackgroundStartPrivileges(gesturePendingIntent);
+ });
+ return true;
+ }
+
+ /**
+ * If gestureTargetActivityPendingIntent is null, this method is invoked to start the activity
+ * that QuickAccessWalletService has defined to host the Wallet view, which is typically the
+ * home screen of the Wallet application.
+ */
+ private void sendFallbackPendingIntent() {
+ mQuickAccessWalletClient.getWalletPendingIntent(
+ getContext().getMainExecutor(),
+ walletPendingIntent -> {
+ if (walletPendingIntent == null) {
+ Slog.w(TAG, "getWalletPendingIntent returns null. Not launching "
+ + "anything for wallet.");
+ return;
+ }
+ sendPendingIntentWithBackgroundStartPrivileges(walletPendingIntent);
+ });
+ }
+
+ private void sendPendingIntentWithBackgroundStartPrivileges(PendingIntent pendingIntent) {
+ try {
+ pendingIntent.send(GRANT_BACKGROUND_START_PRIVILEGES);
+ } catch (PendingIntent.CanceledException e) {
+ Slog.e(TAG, "PendingIntent was canceled", e);
+ }
+ }
+
/**
* @return true if camera was launched, false otherwise.
*/
@@ -700,31 +909,39 @@
Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
}
- private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
- mUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
- mContext.getContentResolver().unregisterContentObserver(mSettingObserver);
- registerContentObservers();
- updateCameraRegistered();
- updateCameraDoubleTapPowerEnabled();
- updateEmergencyGestureEnabled();
- updateEmergencyGesturePowerButtonCooldownPeriodMs();
- }
- }
- };
+ private final BroadcastReceiver mUserReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
+ mUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ mContext.getContentResolver().unregisterContentObserver(mSettingObserver);
+ registerContentObservers();
+ updateCameraRegistered();
+ updateCameraDoubleTapPowerEnabled();
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ updateWalletDoubleTapPowerEnabled();
+ }
+ updateEmergencyGestureEnabled();
+ updateEmergencyGesturePowerButtonCooldownPeriodMs();
+ }
+ }
+ };
- private final ContentObserver mSettingObserver = new ContentObserver(new Handler()) {
- public void onChange(boolean selfChange, android.net.Uri uri, int userId) {
- if (userId == mUserId) {
- updateCameraRegistered();
- updateCameraDoubleTapPowerEnabled();
- updateEmergencyGestureEnabled();
- updateEmergencyGesturePowerButtonCooldownPeriodMs();
- }
- }
- };
+ private final ContentObserver mSettingObserver =
+ new ContentObserver(new Handler()) {
+ public void onChange(boolean selfChange, android.net.Uri uri, int userId) {
+ if (userId == mUserId) {
+ updateCameraRegistered();
+ updateCameraDoubleTapPowerEnabled();
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ updateWalletDoubleTapPowerEnabled();
+ }
+ updateEmergencyGestureEnabled();
+ updateEmergencyGesturePowerButtonCooldownPeriodMs();
+ }
+ }
+ };
private final class GestureEventListener implements SensorEventListener {
@Override
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index e57b009..bd7a0ac 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -449,6 +449,8 @@
private AtomicBoolean mIsSatelliteEnabled;
private AtomicBoolean mWasSatelliteEnabledNotified;
+ private final int mPid = Process.myPid();
+
/**
* Per-phone map of precise data connection state. The key of the map is the pair of transport
* type and APN setting. This is the cache to prevent redundant callbacks to the listeners.
@@ -1441,7 +1443,17 @@
}
if (events.contains(TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)) {
try {
- r.callback.onCallStatesChanged(mCallStateLists.get(r.phoneId));
+ if (Flags.passCopiedCallStateList()) {
+ List<CallState> callList;
+ if (r.callerPid == mPid) {
+ callList = List.copyOf(mCallStateLists.get(r.phoneId));
+ } else {
+ callList = mCallStateLists.get(r.phoneId);
+ }
+ r.callback.onCallStatesChanged(callList);
+ } else {
+ r.callback.onCallStatesChanged(mCallStateLists.get(r.phoneId));
+ }
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -2166,7 +2178,11 @@
overrideNetworkType = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE;
}
boolean isRoaming = telephonyDisplayInfo.isRoaming();
- return new TelephonyDisplayInfo(networkType, overrideNetworkType, isRoaming);
+ boolean isNtn = telephonyDisplayInfo.isNtn();
+ boolean isSatelliteConstrainedData =
+ telephonyDisplayInfo.isSatelliteConstrainedData();
+ return new TelephonyDisplayInfo(networkType, overrideNetworkType, isRoaming,
+ isNtn, isSatelliteConstrainedData);
}
public void notifyCallForwardingChanged(boolean cfi) {
@@ -2569,12 +2585,25 @@
}
if (notifyCallState) {
+ List<CallState> copyList = null;
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)
&& idMatch(r, subId, phoneId)) {
+ // If listener is in the same process, original instance can be passed
+ // to the listener via AIDL without serialization/de-serialization. We
+ // will pass the copied list. Since the element is newly created instead
+ // of modification for the change, we can use shallow copy for this.
try {
- r.callback.onCallStatesChanged(mCallStateLists.get(phoneId));
+ if (Flags.passCopiedCallStateList()) {
+ if (r.callerPid == mPid && copyList == null) {
+ copyList = List.copyOf(mCallStateLists.get(phoneId));
+ }
+ r.callback.onCallStatesChanged(copyList == null
+ ? mCallStateLists.get(phoneId) : copyList);
+ } else {
+ r.callback.onCallStatesChanged(mCallStateLists.get(phoneId));
+ }
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -2902,13 +2931,21 @@
log("There is no active call to report CallQuality");
return;
}
-
+ List<CallState> copyList = null;
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)
&& idMatch(r, subId, phoneId)) {
try {
- r.callback.onCallStatesChanged(mCallStateLists.get(phoneId));
+ if (Flags.passCopiedCallStateList()) {
+ if (r.callerPid == mPid && copyList == null) {
+ copyList = List.copyOf(mCallStateLists.get(phoneId));
+ }
+ r.callback.onCallStatesChanged(copyList == null
+ ? mCallStateLists.get(phoneId) : copyList);
+ } else {
+ r.callback.onCallStatesChanged(mCallStateLists.get(phoneId));
+ }
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 3ce6451..719928b 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -2846,6 +2846,14 @@
: AccountsDb.DEBUG_ACTION_SET_PASSWORD;
logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts,
callingUid);
+
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT,
+ account.type,
+ callingUid,
+ TextUtils.isEmpty(password)
+ ? FrameworkStatsLog.ACCOUNT_MANAGER_EVENT__EVENT_TYPE__PASSWORD_REMOVED
+ : FrameworkStatsLog.ACCOUNT_MANAGER_EVENT__EVENT_TYPE__PASSWORD_CHANGED);
}
} finally {
accounts.accountsDb.endTransaction();
@@ -2912,7 +2920,7 @@
if (!accountExistsCache(accounts, account)) {
return;
}
- setUserdataInternal(accounts, account, key, value);
+ setUserdataInternal(accounts, account, key, value, callingUid);
} finally {
restoreCallingIdentity(identityToken);
}
@@ -2932,7 +2940,7 @@
}
private void setUserdataInternal(UserAccounts accounts, Account account, String key,
- String value) {
+ String value, int callingUid) {
synchronized (accounts.dbLock) {
accounts.accountsDb.beginTransaction();
try {
@@ -2958,6 +2966,11 @@
AccountManager.invalidateLocalAccountUserDataCaches();
}
}
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT,
+ account.type,
+ callingUid,
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT__EVENT_TYPE__USER_DATA_CHANGED);
}
private void onResult(IAccountManagerResponse response, Bundle result) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f41aaee..cd929c1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -361,6 +361,8 @@
import android.os.incremental.IIncrementalService;
import android.os.incremental.IncrementalManager;
import android.os.incremental.IncrementalMetrics;
+import android.os.instrumentation.IOffsetCallback;
+import android.os.instrumentation.MethodDescriptor;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.provider.DeviceConfig;
@@ -509,6 +511,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
@@ -2781,9 +2784,7 @@
mIsolatedAppBindArgs = new ArrayMap<>(1);
// See b/79378449 about the following exemption.
addServiceToMap(mIsolatedAppBindArgs, "package");
- if (!android.server.Flags.removeJavaServiceManagerCache()) {
- addServiceToMap(mIsolatedAppBindArgs, "permissionmgr");
- }
+ addServiceToMap(mIsolatedAppBindArgs, "permissionmgr");
}
return mIsolatedAppBindArgs;
}
@@ -2794,38 +2795,27 @@
// Add common services.
// IMPORTANT: Before adding services here, make sure ephemeral apps can access them too.
// Enable the check in ApplicationThread.bindApplication() to make sure.
-
- // Removing User Service and App Ops Service from cache breaks boot for auto.
- // Removing permissionmgr breaks tests for Android Auto due to SELinux restrictions.
- // TODO: fix SELinux restrictions and remove caching for Android Auto.
- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
- || !android.server.Flags.removeJavaServiceManagerCache()) {
- addServiceToMap(mAppBindArgs, Context.ALARM_SERVICE);
- addServiceToMap(mAppBindArgs, Context.DISPLAY_SERVICE);
- addServiceToMap(mAppBindArgs, Context.NETWORKMANAGEMENT_SERVICE);
- addServiceToMap(mAppBindArgs, Context.CONNECTIVITY_SERVICE);
- addServiceToMap(mAppBindArgs, Context.ACCESSIBILITY_SERVICE);
- addServiceToMap(mAppBindArgs, Context.INPUT_METHOD_SERVICE);
- addServiceToMap(mAppBindArgs, Context.INPUT_SERVICE);
- addServiceToMap(mAppBindArgs, "graphicsstats");
- addServiceToMap(mAppBindArgs, "content");
- addServiceToMap(mAppBindArgs, Context.JOB_SCHEDULER_SERVICE);
- addServiceToMap(mAppBindArgs, Context.NOTIFICATION_SERVICE);
- addServiceToMap(mAppBindArgs, Context.VIBRATOR_SERVICE);
- addServiceToMap(mAppBindArgs, Context.ACCOUNT_SERVICE);
- addServiceToMap(mAppBindArgs, Context.POWER_SERVICE);
- addServiceToMap(mAppBindArgs, "mount");
- addServiceToMap(mAppBindArgs, Context.PLATFORM_COMPAT_SERVICE);
- }
- // See b/79378449
- // Getting the window service and package service binder from servicemanager
- // is blocked for Apps. However they are necessary for apps.
- // TODO: remove exception
addServiceToMap(mAppBindArgs, "package");
- addServiceToMap(mAppBindArgs, Context.WINDOW_SERVICE);
- addServiceToMap(mAppBindArgs, Context.USER_SERVICE);
addServiceToMap(mAppBindArgs, "permissionmgr");
+ addServiceToMap(mAppBindArgs, Context.WINDOW_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.ALARM_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.DISPLAY_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.NETWORKMANAGEMENT_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.CONNECTIVITY_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.ACCESSIBILITY_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.INPUT_METHOD_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.INPUT_SERVICE);
+ addServiceToMap(mAppBindArgs, "graphicsstats");
addServiceToMap(mAppBindArgs, Context.APP_OPS_SERVICE);
+ addServiceToMap(mAppBindArgs, "content");
+ addServiceToMap(mAppBindArgs, Context.JOB_SCHEDULER_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.NOTIFICATION_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.VIBRATOR_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.ACCOUNT_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.POWER_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.USER_SERVICE);
+ addServiceToMap(mAppBindArgs, "mount");
+ addServiceToMap(mAppBindArgs, Context.PLATFORM_COMPAT_SERVICE);
}
return mAppBindArgs;
}
@@ -18055,6 +18045,26 @@
}
@Override
+ public void getExecutableMethodFileOffsets(@NonNull String processName,
+ int pid, int uid, @NonNull MethodDescriptor methodDescriptor,
+ @NonNull IOffsetCallback callback) {
+ final IApplicationThread thread;
+ synchronized (ActivityManagerService.this) {
+ ProcessRecord record = mProcessList.getProcessRecordLocked(processName, uid);
+ if (record == null || record.getPid() != pid) {
+ throw new NoSuchElementException();
+ }
+ thread = record.getThread();
+ }
+ try {
+ thread.getExecutableMethodFileOffsets(methodDescriptor, callback);
+ } catch (RemoteException e) {
+ throw new RuntimeException(
+ "IApplicationThread.getExecutableMethodFileOffsets failed", e);
+ }
+ }
+
+ @Override
public void addCreatorToken(Intent intent, String creatorPackage) {
ActivityManagerService.this.addCreatorToken(intent, creatorPackage);
}
@@ -19334,7 +19344,8 @@
if (!preventIntentRedirect()) return;
if (intent == null) return;
- if ((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED) == 0) {
+ if (((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED) == 0)
+ && intent.getExtras() != null && intent.getExtras().hasIntent()) {
Slog.wtf(TAG,
"[IntentRedirect] The intent does not have its nested keys collected as a "
+ "preparation for creating intent creator tokens. Intent: "
@@ -19405,7 +19416,7 @@
return createOrGetIntentCreatorToken(intent, key);
} else {
- throw new IllegalArgumentException("intent does not contain a creator token.");
+ return null;
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 400ebfd..c27126a 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1251,12 +1251,10 @@
}
private static float clampPowerMah(double powerMah, String consumer) {
- float resultPowerMah = 0;
- if (powerMah <= Float.MAX_VALUE && powerMah >= Float.MIN_VALUE) {
- resultPowerMah = (float) powerMah;
- } else {
- // Handle overflow appropriately
- Slog.wtfStack(TAG, consumer + " reported powerMah float overflow: " + powerMah);
+ float resultPowerMah = Double.valueOf(powerMah).floatValue();
+ if (Float.isInfinite(resultPowerMah)) {
+ resultPowerMah = 0;
+ Slog.d(TAG, consumer + " reported powerMah float overflow : " + powerMah);
}
return resultPowerMah;
}
@@ -1361,11 +1359,10 @@
final String powerComponentName = batteryConsumer.getPowerComponentName(componentId);
final double consumedPowerMah = batteryConsumer.getConsumedPower(key);
- float powerMah =
+ final float powerMah =
clampPowerMah(
- consumedPowerMah, "uidConsumer-" + uid + "-" + powerComponentName);
+ consumedPowerMah, "uid-" + uid + "-" + powerComponentName);
final long powerComponentDurationMillis = batteryConsumer.getUsageDurationMillis(key);
-
if (powerMah == 0 && powerComponentDurationMillis == 0) {
return true;
}
diff --git a/services/core/java/com/android/server/am/BroadcastFilter.java b/services/core/java/com/android/server/am/BroadcastFilter.java
index 05aeb42..8327639 100644
--- a/services/core/java/com/android/server/am/BroadcastFilter.java
+++ b/services/core/java/com/android/server/am/BroadcastFilter.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledSince;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.os.Binder;
@@ -41,7 +40,6 @@
* ({@link IntentFilter#SYSTEM_LOW_PRIORITY}, {@link IntentFilter#SYSTEM_HIGH_PRIORITY}).
*/
@ChangeId
- @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.BASE)
@VisibleForTesting
static final long RESTRICT_PRIORITY_VALUES = 371309185L;
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index a1ab1eea..8d0805d 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -43,7 +43,6 @@
import android.app.BroadcastOptions;
import android.app.BroadcastOptions.DeliveryGroupPolicy;
import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledSince;
import android.content.ComponentName;
import android.content.IIntentReceiver;
import android.content.Intent;
@@ -86,7 +85,6 @@
* will only influence the order of broadcast delivery within the same process.
*/
@ChangeId
- @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.BASE)
@VisibleForTesting
static final long LIMIT_PRIORITY_SCOPE = 371307720L;
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index d0153d8..883e09f 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -42,6 +42,7 @@
import static com.android.aconfig_new_storage.Flags.enableAconfigStorageDaemon;
import static com.android.aconfig_new_storage.Flags.supportImmediateLocalOverrides;
import static com.android.aconfig_new_storage.Flags.supportClearLocalOverridesImmediately;
+import static com.android.aconfig_new_storage.Flags.enableAconfigdFromMainline;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -146,7 +147,9 @@
"aaos_window_triage",
"aaos_audio_triage",
"aaos_power_triage",
+ "aaos_storage_triage",
"aaos_sdv",
+ "aaos_vac_triage",
"accessibility",
"android_core_networking",
"android_health_services",
@@ -213,6 +216,7 @@
"pixel_bluetooth",
"pixel_connectivity_gps",
"pixel_continuity",
+ "pixel_display",
"pixel_perf",
"pixel_sensors",
"pixel_state_server",
@@ -462,16 +466,38 @@
* @param requests: request proto output stream
* @return aconfigd socket return as proto input stream
*/
- static ProtoInputStream sendAconfigdRequests(ProtoOutputStream requests) {
+ static void sendAconfigdRequests(ProtoOutputStream requests) {
+ ProtoInputStream returns = sendAconfigdRequests("aconfigd_system", requests);
+ try {
+ parseAndLogAconfigdReturn(returns);
+ } catch (IOException ioe) {
+ logErr("failed to parse aconfigd return", ioe);
+ }
+ if (enableAconfigdFromMainline()) {
+ returns = sendAconfigdRequests("aconfigd_mainline", requests);
+ try {
+ parseAndLogAconfigdReturn(returns);
+ } catch (IOException ioe) {
+ logErr("failed to parse aconfigd return", ioe);
+ }
+ }
+ }
+
+ /**
+ * apply flag local override in aconfig new storage
+ * @param socketName: the socket to send to
+ * @param requests: request proto output stream
+ * @return aconfigd socket return as proto input stream
+ */
+ static ProtoInputStream sendAconfigdRequests(String socketName, ProtoOutputStream requests) {
// connect to aconfigd socket
LocalSocket client = new LocalSocket();
- String socketName = "aconfigd_system";
try {
client.connect(new LocalSocketAddress(
socketName, LocalSocketAddress.Namespace.RESERVED));
- Slog.d(TAG, "connected to aconfigd socket");
+ Slog.d(TAG, "connected to " + socketName + " socket");
} catch (IOException ioe) {
- logErr("failed to connect to aconfigd socket", ioe);
+ logErr("failed to connect to " + socketName + " socket", ioe);
return null;
}
@@ -490,9 +516,9 @@
byte[] requests_bytes = requests.getBytes();
outputStream.writeInt(requests_bytes.length);
outputStream.write(requests_bytes, 0, requests_bytes.length);
- Slog.d(TAG, "flag override requests sent to aconfigd");
+ Slog.d(TAG, "flag override requests sent to " + socketName);
} catch (IOException ioe) {
- logErr("failed to send requests to aconfigd", ioe);
+ logErr("failed to send requests to " + socketName, ioe);
return null;
}
@@ -500,10 +526,10 @@
try {
int num_bytes = inputStream.readInt();
ProtoInputStream returns = new ProtoInputStream(inputStream);
- Slog.d(TAG, "received " + num_bytes + " bytes back from aconfigd");
+ Slog.d(TAG, "received " + num_bytes + " bytes back from " + socketName);
return returns;
} catch (IOException ioe) {
- logErr("failed to read requests return from aconfigd", ioe);
+ logErr("failed to read requests return from " + socketName, ioe);
return null;
}
}
@@ -644,15 +670,8 @@
return;
}
- // send requests to aconfigd and obtain the return byte buffer
- ProtoInputStream returns = sendAconfigdRequests(requests);
-
- // deserialize back using proto input stream
- try {
- parseAndLogAconfigdReturn(returns);
- } catch (IOException ioe) {
- logErr("failed to parse aconfigd return", ioe);
- }
+ // send requests to aconfigd
+ sendAconfigdRequests(requests);
}
public static SettingsToPropertiesMapper start(ContentResolver contentResolver) {
@@ -762,15 +781,8 @@
return;
}
- // send requests to aconfigd and obtain the return
- ProtoInputStream returns = sendAconfigdRequests(requests);
-
- // deserialize back using proto input stream
- try {
- parseAndLogAconfigdReturn(returns);
- } catch (IOException ioe) {
- logErr("failed to parse aconfigd return", ioe);
- }
+ // send requests to aconfigd
+ sendAconfigdRequests(requests);
}
/**
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 295e044..06c586f 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3191,7 +3191,7 @@
resolveProxyPackageName, proxyAttributionTag, proxyVirtualDeviceId,
Process.INVALID_UID, null, null,
Context.DEVICE_ID_DEFAULT, proxyFlags, !isProxyTrusted,
- "proxy " + message, shouldCollectMessage, 1);
+ "proxy " + message, shouldCollectMessage);
if (proxyReturn.getOpMode() != AppOpsManager.MODE_ALLOWED) {
return new SyncNotedAppOp(proxyReturn.getOpMode(), code, proxiedAttributionTag,
proxiedPackageName);
@@ -3210,20 +3210,7 @@
return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName,
proxiedAttributionTag, proxiedVirtualDeviceId, proxyUid, resolveProxyPackageName,
proxyAttributionTag, proxyVirtualDeviceId, proxiedFlags, shouldCollectAsyncNotedOp,
- message, shouldCollectMessage, 1);
- }
-
- @Override
- public void noteOperationsInBatch(Map batchedNoteOps) {
- for (var entry : ((Map<AppOpsManager.NotedOp, Integer>) batchedNoteOps).entrySet()) {
- AppOpsManager.NotedOp notedOp = entry.getKey();
- int notedCount = entry.getValue();
- mCheckOpsDelegateDispatcher.noteOperation(
- notedOp.getOp(), notedOp.getUid(), notedOp.getPackageName(),
- notedOp.getAttributionTag(), notedOp.getVirtualDeviceId(),
- notedOp.getShouldCollectAsyncNotedOp(), notedOp.getMessage(),
- notedOp.getShouldCollectMessage(), notedCount);
- }
+ message, shouldCollectMessage);
}
@Override
@@ -3241,7 +3228,7 @@
}
return mCheckOpsDelegateDispatcher.noteOperation(code, uid, packageName,
attributionTag, Context.DEVICE_ID_DEFAULT, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, 1);
+ shouldCollectMessage);
}
@Override
@@ -3250,12 +3237,13 @@
String message, boolean shouldCollectMessage) {
return mCheckOpsDelegateDispatcher.noteOperation(code, uid, packageName,
attributionTag, virtualDeviceId, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, 1);
+ shouldCollectMessage);
}
private SyncNotedAppOp noteOperationImpl(int code, int uid, @Nullable String packageName,
- @Nullable String attributionTag, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
- @Nullable String message, boolean shouldCollectMessage, int notedCount) {
+ @Nullable String attributionTag, int virtualDeviceId,
+ boolean shouldCollectAsyncNotedOp, @Nullable String message,
+ boolean shouldCollectMessage) {
String resolvedPackageName;
if (!shouldUseNewCheckOp()) {
verifyIncomingUid(uid);
@@ -3290,14 +3278,14 @@
return noteOperationUnchecked(code, uid, resolvedPackageName, attributionTag,
virtualDeviceId, Process.INVALID_UID, null, null,
Context.DEVICE_ID_DEFAULT, AppOpsManager.OP_FLAG_SELF, shouldCollectAsyncNotedOp,
- message, shouldCollectMessage, notedCount);
+ message, shouldCollectMessage);
}
private SyncNotedAppOp noteOperationUnchecked(int code, int uid, @NonNull String packageName,
@Nullable String attributionTag, int virtualDeviceId, int proxyUid,
String proxyPackageName, @Nullable String proxyAttributionTag, int proxyVirtualDeviceId,
@OpFlags int flags, boolean shouldCollectAsyncNotedOp, @Nullable String message,
- boolean shouldCollectMessage, int notedCount) {
+ boolean shouldCollectMessage) {
PackageVerificationResult pvr;
try {
pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
@@ -3400,11 +3388,11 @@
virtualDeviceId, flags, AppOpsManager.MODE_ALLOWED);
attributedOp.accessed(proxyUid, proxyPackageName, proxyAttributionTag,
- getPersistentId(proxyVirtualDeviceId), uidState.getState(), flags, notedCount);
+ getPersistentId(proxyVirtualDeviceId), uidState.getState(), flags);
if (shouldCollectAsyncNotedOp) {
collectAsyncNotedOp(uid, packageName, code, attributionTag, flags, message,
- shouldCollectMessage, notedCount);
+ shouldCollectMessage);
}
return new SyncNotedAppOp(AppOpsManager.MODE_ALLOWED, code, attributionTag,
@@ -3563,7 +3551,7 @@
*/
private void collectAsyncNotedOp(int uid, @NonNull String packageName, int opCode,
@Nullable String attributionTag, @OpFlags int flags, @NonNull String message,
- boolean shouldCollectMessage, int notedCount) {
+ boolean shouldCollectMessage) {
Objects.requireNonNull(message);
int callingUid = Binder.getCallingUid();
@@ -3571,51 +3559,42 @@
final long token = Binder.clearCallingIdentity();
try {
synchronized (this) {
+ Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid);
+
+ RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
+ AsyncNotedAppOp asyncNotedOp = new AsyncNotedAppOp(opCode, callingUid,
+ attributionTag, message, System.currentTimeMillis());
+ final boolean[] wasNoteForwarded = {false};
+
if ((flags & (OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED)) != 0
&& shouldCollectMessage) {
reportRuntimeAppOpAccessMessageAsyncLocked(uid, packageName, opCode,
attributionTag, message);
}
- Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid);
- RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
- if (callbacks == null) {
- return;
- }
-
- final boolean[] wasNoteForwarded = {false};
- if (Flags.rateLimitBatchedNoteOpAsyncCallbacksEnabled()) {
- notedCount = 1;
- }
-
- for (int i = 0; i < notedCount; i++) {
- AsyncNotedAppOp asyncNotedOp = new AsyncNotedAppOp(opCode, callingUid,
- attributionTag, message, System.currentTimeMillis());
- wasNoteForwarded[0] = false;
+ if (callbacks != null) {
callbacks.broadcast((cb) -> {
try {
cb.opNoted(asyncNotedOp);
wasNoteForwarded[0] = true;
} catch (RemoteException e) {
Slog.e(TAG,
- "Could not forward noteOp of " + opCode + " to "
- + packageName
+ "Could not forward noteOp of " + opCode + " to " + packageName
+ "/" + uid + "(" + attributionTag + ")", e);
}
});
+ }
- if (!wasNoteForwarded[0]) {
- ArrayList<AsyncNotedAppOp> unforwardedOps = mUnforwardedAsyncNotedOps.get(
- key);
- if (unforwardedOps == null) {
- unforwardedOps = new ArrayList<>(1);
- mUnforwardedAsyncNotedOps.put(key, unforwardedOps);
- }
+ if (!wasNoteForwarded[0]) {
+ ArrayList<AsyncNotedAppOp> unforwardedOps = mUnforwardedAsyncNotedOps.get(key);
+ if (unforwardedOps == null) {
+ unforwardedOps = new ArrayList<>(1);
+ mUnforwardedAsyncNotedOps.put(key, unforwardedOps);
+ }
- unforwardedOps.add(asyncNotedOp);
- if (unforwardedOps.size() > MAX_UNFORWARDED_OPS) {
- unforwardedOps.remove(0);
- }
+ unforwardedOps.add(asyncNotedOp);
+ if (unforwardedOps.size() > MAX_UNFORWARDED_OPS) {
+ unforwardedOps.remove(0);
}
}
}
@@ -4047,7 +4026,7 @@
if (shouldCollectAsyncNotedOp && !isRestricted) {
collectAsyncNotedOp(uid, packageName, code, attributionTag, AppOpsManager.OP_FLAG_SELF,
- message, shouldCollectMessage, 1);
+ message, shouldCollectMessage);
}
return new SyncNotedAppOp(isRestricted ? MODE_IGNORED : MODE_ALLOWED, code, attributionTag,
@@ -7595,36 +7574,34 @@
public SyncNotedAppOp noteOperation(int code, int uid, String packageName,
String attributionTag, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
- String message, boolean shouldCollectMessage, int notedCount) {
+ String message, boolean shouldCollectMessage) {
if (mPolicy != null) {
if (mCheckOpsDelegate != null) {
return mPolicy.noteOperation(code, uid, packageName, attributionTag,
virtualDeviceId, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, notedCount, this::noteDelegateOperationImpl
+ shouldCollectMessage, this::noteDelegateOperationImpl
);
} else {
return mPolicy.noteOperation(code, uid, packageName, attributionTag,
virtualDeviceId, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, notedCount, AppOpsService.this::noteOperationImpl
+ shouldCollectMessage, AppOpsService.this::noteOperationImpl
);
}
} else if (mCheckOpsDelegate != null) {
return noteDelegateOperationImpl(code, uid, packageName, attributionTag,
- virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- notedCount);
+ virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
}
return noteOperationImpl(code, uid, packageName, attributionTag,
- virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- notedCount);
+ virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
}
private SyncNotedAppOp noteDelegateOperationImpl(int code, int uid,
@Nullable String packageName, @Nullable String featureId, int virtualDeviceId,
boolean shouldCollectAsyncNotedOp, @Nullable String message,
- boolean shouldCollectMessage, int notedCount) {
+ boolean shouldCollectMessage) {
return mCheckOpsDelegate.noteOperation(code, uid, packageName, featureId,
virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- notedCount, AppOpsService.this::noteOperationImpl
+ AppOpsService.this::noteOperationImpl
);
}
diff --git a/services/core/java/com/android/server/appop/AttributedOp.java b/services/core/java/com/android/server/appop/AttributedOp.java
index 4d114b4..314664b 100644
--- a/services/core/java/com/android/server/appop/AttributedOp.java
+++ b/services/core/java/com/android/server/appop/AttributedOp.java
@@ -100,12 +100,10 @@
* @param proxyDeviceId The device Id of the proxy
* @param uidState UID state of the app noteOp/startOp was called for
* @param flags OpFlags of the call
- * @param accessCount The number of times the op is accessed
*/
public void accessed(int proxyUid, @Nullable String proxyPackageName,
@Nullable String proxyAttributionTag, @Nullable String proxyDeviceId,
- @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags,
- int accessCount) {
+ @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags) {
long accessTime = System.currentTimeMillis();
accessed(accessTime, -1, proxyUid, proxyPackageName, proxyAttributionTag, proxyDeviceId,
uidState, flags);
@@ -113,7 +111,7 @@
mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
parent.packageName, persistentDeviceId, tag, uidState, flags, accessTime,
AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE,
- DiscreteRegistry.ACCESS_TYPE_NOTE_OP, accessCount);
+ DiscreteRegistry.ACCESS_TYPE_NOTE_OP);
}
/**
@@ -257,7 +255,7 @@
if (isStarted) {
mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
parent.packageName, persistentDeviceId, tag, uidState, flags, startTime,
- attributionFlags, attributionChainId, DiscreteRegistry.ACCESS_TYPE_START_OP, 1);
+ attributionFlags, attributionChainId, DiscreteRegistry.ACCESS_TYPE_START_OP);
}
}
@@ -453,7 +451,7 @@
mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
parent.packageName, persistentDeviceId, tag, event.getUidState(),
event.getFlags(), startTime, event.getAttributionFlags(),
- event.getAttributionChainId(), DiscreteRegistry.ACCESS_TYPE_RESUME_OP, 1);
+ event.getAttributionChainId(), DiscreteRegistry.ACCESS_TYPE_RESUME_OP);
if (shouldSendActive) {
mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
parent.packageName, tag, event.getVirtualDeviceId(), true,
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 5e67f26..6b02538 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -475,7 +475,7 @@
@NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState,
@OpFlags int flags, long accessTime,
@AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
- @DiscreteRegistry.AccessType int accessType, int accessCount) {
+ @DiscreteRegistry.AccessType int accessType) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
if (!isPersistenceInitializedMLocked()) {
@@ -484,7 +484,7 @@
}
getUpdatedPendingHistoricalOpsMLocked(
System.currentTimeMillis()).increaseAccessCount(op, uid, packageName,
- attributionTag, uidState, flags, accessCount);
+ attributionTag, uidState, flags, 1);
mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op,
attributionTag, flags, uidState, accessTime, -1, attributionFlags,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 2f7a54d..09b8e21 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -95,6 +95,7 @@
import android.app.BroadcastOptions;
import android.app.IUidObserver;
import android.app.NotificationManager;
+import android.app.PropertyInvalidatedCache;
import android.app.UidObserver;
import android.app.role.OnRoleHoldersChangedListener;
import android.app.role.RoleManager;
@@ -248,6 +249,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
@@ -4175,12 +4177,6 @@
// Stream mute changed, fire the intent.
Intent intent = new Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION);
intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, isMuted);
- if (replaceStreamBtSco() && isStreamBluetoothSco(streamType)) {
- intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
- AudioSystem.STREAM_BLUETOOTH_SCO);
- // in this case broadcast for both sco and voice_call streams the mute status
- sendBroadcastToAll(intent, null /* options */);
- }
intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
sendBroadcastToAll(intent, null /* options */);
}
@@ -4688,6 +4684,12 @@
switch (mode) {
case AudioSystem.MODE_IN_COMMUNICATION:
case AudioSystem.MODE_IN_CALL:
+ // TODO(b/382704431): remove to allow STREAM_VOICE_CALL to drive abs volume
+ // over A2DP
+ if (getDeviceForStream(AudioSystem.STREAM_VOICE_CALL)
+ == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
+ return AudioSystem.STREAM_MUSIC;
+ }
return AudioSystem.STREAM_VOICE_CALL;
case AudioSystem.MODE_CALL_SCREENING:
case AudioSystem.MODE_COMMUNICATION_REDIRECT:
@@ -4699,15 +4701,20 @@
// other conditions will influence the stream type choice, read on...
break;
}
- if (voiceActivityCanOverride
- && mVoicePlaybackActive.get()) {
+
+ if (voiceActivityCanOverride && mVoicePlaybackActive.get()) {
+ // TODO(b/382704431): remove to allow STREAM_VOICE_CALL to drive abs volume over A2DP
+ if (getDeviceForStream(AudioSystem.STREAM_VOICE_CALL)
+ == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
+ return AudioSystem.STREAM_MUSIC;
+ }
return AudioSystem.STREAM_VOICE_CALL;
}
return AudioSystem.STREAM_MUSIC;
}
- private AtomicBoolean mVoicePlaybackActive = new AtomicBoolean(false);
- private AtomicBoolean mMediaPlaybackActive = new AtomicBoolean(false);
+ private final AtomicBoolean mVoicePlaybackActive = new AtomicBoolean(false);
+ private final AtomicBoolean mMediaPlaybackActive = new AtomicBoolean(false);
private final IPlaybackConfigDispatcher mPlaybackActivityMonitor =
new IPlaybackConfigDispatcher.Stub() {
@@ -4927,7 +4934,7 @@
private void onUpdateContextualVolumes() {
final int streamType = getBluetoothContextualVolumeStream();
- Log.i(TAG,
+ Slog.i(TAG,
"onUpdateContextualVolumes: absolute volume driving streams " + streamType
+ " avrcp supported: " + mAvrcpAbsVolSupported);
synchronized (mCachedAbsVolDrivingStreamsLock) {
@@ -4936,7 +4943,7 @@
if (absDev == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
enabled = mAvrcpAbsVolSupported;
if (!enabled) {
- Log.w(TAG, "Updating avrcp not supported in onUpdateContextualVolumes");
+ Slog.w(TAG, "Updating avrcp not supported in onUpdateContextualVolumes");
}
}
if (stream != streamType) {
@@ -4964,7 +4971,7 @@
return;
}
if (absVolumeDevices.size() > 1) {
- Log.w(TAG, "onUpdateContextualVolumes too many active devices: "
+ Slog.w(TAG, "onUpdateContextualVolumes too many active devices: "
+ absVolumeDevices.stream().map(AudioSystem::getOutputDeviceName)
.collect(Collectors.joining(","))
+ ", for stream: " + streamType);
@@ -4975,7 +4982,7 @@
final int index = getStreamVolume(streamType, device);
if (DEBUG_VOL) {
- Log.i(TAG, "onUpdateContextualVolumes streamType: " + streamType
+ Slog.i(TAG, "onUpdateContextualVolumes streamType: " + streamType
+ ", device: " + AudioSystem.getOutputDeviceName(device)
+ ", index: " + index);
}
@@ -9663,16 +9670,9 @@
mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
mVolumeChanged.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE,
oldIndex);
- int extraStreamType = mStreamType;
- // TODO: remove this when deprecating STREAM_BLUETOOTH_SCO
- if (isStreamBluetoothSco(mStreamType)) {
- mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
- AudioSystem.STREAM_BLUETOOTH_SCO);
- extraStreamType = AudioSystem.STREAM_BLUETOOTH_SCO;
- } else {
- mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
- mStreamType);
- }
+
+ mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
+ mStreamType);
mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE_ALIAS,
streamAlias);
@@ -9683,21 +9683,9 @@
" aliased streams: " + aliasStreamIndexes;
}
AudioService.sVolumeLogger.enqueue(new VolChangedBroadcastEvent(
- extraStreamType, aliasStreamIndexesString, index, oldIndex));
- if (extraStreamType != mStreamType) {
- AudioService.sVolumeLogger.enqueue(new VolChangedBroadcastEvent(
- mStreamType, aliasStreamIndexesString, index, oldIndex));
- }
+ mStreamType, aliasStreamIndexesString, index, oldIndex));
}
sendBroadcastToAll(mVolumeChanged, mVolumeChangedOptions);
- if (extraStreamType != mStreamType) {
- // send multiple intents in case we merged voice call and bt sco streams
- mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
- mStreamType);
- // do not use the options in thid case which could discard
- // the previous intent
- sendBroadcastToAll(mVolumeChanged, null);
- }
}
}
}
@@ -10941,14 +10929,122 @@
}
};
mSysPropListenerNativeHandle = mAudioSystem.listenForSystemPropertyChange(
- PermissionManager.CACHE_KEY_PACKAGE_INFO,
+ PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY,
task);
} else {
mAudioSystem.listenForSystemPropertyChange(
- PermissionManager.CACHE_KEY_PACKAGE_INFO,
+ PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY,
() -> mAudioServerLifecycleExecutor.execute(
mPermissionProvider::onPermissionStateChanged));
}
+
+ if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
+ new PackageInfoTransducer().start();
+ }
+ }
+
+ /**
+ * A transducer that converts high-speed changes in the CACHE_KEY_PACKAGE_INFO_CACHE
+ * PropertyInvalidatedCache into low-speed changes in the CACHE_KEY_PACKAGE_INFO_NOTIFY system
+ * property. This operates on the popcorn principle: changes in the source are done when the
+ * source has been quiet for the soak interval.
+ *
+ * TODO(b/381097912) This is a temporary measure to support migration away from sysprop
+ * sniffing. It should be cleaned up.
+ */
+ private static class PackageInfoTransducer extends Thread {
+
+ // The run/stop signal.
+ private final AtomicBoolean mRunning = new AtomicBoolean(false);
+
+ // The source of change information.
+ private final PropertyInvalidatedCache.NonceWatcher mWatcher;
+
+ // The handler for scheduling delayed reactions to changes.
+ private final Handler mHandler;
+
+ // How long to soak changes: 50ms is the legacy choice.
+ private final static long SOAK_TIME_MS = 50;
+
+ // The ubiquitous lock.
+ private final Object mLock = new Object();
+
+ // If positive, this is the soak expiration time.
+ @GuardedBy("mLock")
+ private long mSoakDeadlineMs = -1;
+
+ // A source of unique long values.
+ @GuardedBy("mLock")
+ private long mToken = 0;
+
+ PackageInfoTransducer() {
+ mWatcher = PropertyInvalidatedCache
+ .getNonceWatcher(PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE);
+ mHandler = new Handler(BackgroundThread.getHandler().getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ PackageInfoTransducer.this.handleMessage(msg);
+ }};
+ }
+
+ public void run() {
+ mRunning.set(true);
+ while (mRunning.get()) {
+ try {
+ final int changes = mWatcher.waitForChange();
+ if (changes == 0 || !mRunning.get()) {
+ continue;
+ }
+ } catch (InterruptedException e) {
+ // We don't know why the exception occurred but keep running until told to
+ // stop.
+ continue;
+ }
+ trigger();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void updateLocked() {
+ String n = Long.toString(mToken++);
+ SystemProperties.set(PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY, n);
+ }
+
+ private void trigger() {
+ synchronized (mLock) {
+ boolean alreadyQueued = mSoakDeadlineMs >= 0;
+ final long nowMs = SystemClock.uptimeMillis();
+ mSoakDeadlineMs = nowMs + SOAK_TIME_MS;
+ if (!alreadyQueued) {
+ mHandler.sendEmptyMessageAtTime(0, mSoakDeadlineMs);
+ updateLocked();
+ }
+ }
+ }
+
+ private void handleMessage(Message msg) {
+ synchronized (mLock) {
+ if (mSoakDeadlineMs < 0) {
+ return; // ???
+ }
+ final long nowMs = SystemClock.uptimeMillis();
+ if (mSoakDeadlineMs > nowMs) {
+ mSoakDeadlineMs = nowMs + SOAK_TIME_MS;
+ mHandler.sendEmptyMessageAtTime(0, mSoakDeadlineMs);
+ return;
+ }
+ mSoakDeadlineMs = -1;
+ updateLocked();
+ }
+ }
+
+ /**
+ * Cause the thread to exit. Running is set to false and the watcher is awakened.
+ */
+ public void done() {
+ mRunning.set(false);
+ mWatcher.wakeUp();
+ }
}
//==========================================================================================
diff --git a/services/core/java/com/android/server/content/SyncLogger.java b/services/core/java/com/android/server/content/SyncLogger.java
index fc20ef20..7154bc4 100644
--- a/services/core/java/com/android/server/content/SyncLogger.java
+++ b/services/core/java/com/android/server/content/SyncLogger.java
@@ -73,6 +73,8 @@
*/
public static synchronized SyncLogger getInstance() {
if (sInstance == null) {
+ // Always default to the sync logger for now (see b/381957278#comment8).
+ /*
final String flag = SystemProperties.get("debug.synclog");
final boolean enable =
(Build.IS_DEBUGGABLE
@@ -83,6 +85,8 @@
} else {
sInstance = new SyncLogger();
}
+ */
+ sInstance = new SyncLogger();
}
return sInstance;
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 2513443..5d9db65 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -19,17 +19,19 @@
import static android.Manifest.permission.CONTROL_DEVICE_STATE;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED;
-import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN;
-import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN;
import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FEATURE_DUAL_DISPLAY;
import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FEATURE_REAR_DISPLAY;
import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN;
import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT;
import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT;
import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED;
import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST;
import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS;
import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP;
@@ -98,7 +100,6 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.WeakHashMap;
@@ -854,7 +855,7 @@
}
}
- private void requestStateInternal(int state, int flags, int callingPid, int callingUid,
+ private void requestStateInternal(int requestedState, int flags, int callingPid, int callingUid,
@NonNull IBinder token, boolean hasControlDeviceStatePermission) {
synchronized (mLock) {
final ProcessRecord processRecord = mProcessRecords.get(callingPid);
@@ -869,19 +870,30 @@
+ " token: " + token);
}
- final Optional<DeviceState> deviceState = getStateLocked(state);
- if (!deviceState.isPresent()) {
- throw new IllegalArgumentException("Requested state: " + state
+ final Optional<DeviceState> requestedDeviceState = getStateLocked(requestedState);
+ if (requestedDeviceState.isEmpty()) {
+ throw new IllegalArgumentException("Requested state: " + requestedState
+ " is not supported.");
}
- OverrideRequest request = new OverrideRequest(token, callingPid, callingUid,
- deviceState.get(), flags, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+ final OverrideRequest request = new OverrideRequest(token, callingPid, callingUid,
+ requestedDeviceState.get(), flags, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
if (Flags.deviceStatePropertyMigration()) {
- // If we don't have the CONTROL_DEVICE_STATE permission, we want to show the overlay
- if (!hasControlDeviceStatePermission && deviceState.get().hasProperty(
- PROPERTY_FEATURE_REAR_DISPLAY)) {
+ final boolean isRequestingRdm = requestedDeviceState.get()
+ .hasProperty(PROPERTY_FEATURE_REAR_DISPLAY);
+ final boolean isRequestingRdmOuterDefault = requestedDeviceState.get()
+ .hasProperty(PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT);
+
+ final boolean isDeviceClosed = mCommittedState.isEmpty() ? false
+ : mCommittedState.get().hasProperty(
+ PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED);
+
+ final boolean shouldShowRdmEduDialog = isRequestingRdm && shouldShowRdmEduDialog(
+ hasControlDeviceStatePermission, isRequestingRdmOuterDefault,
+ isDeviceClosed);
+
+ if (shouldShowRdmEduDialog) {
showRearDisplayEducationalOverlayLocked(request);
} else {
mOverrideRequestController.addRequest(request);
@@ -889,7 +901,7 @@
} else {
// If we don't have the CONTROL_DEVICE_STATE permission, we want to show the overlay
if (!hasControlDeviceStatePermission && mRearDisplayState != null
- && state == mRearDisplayState.getIdentifier()) {
+ && requestedState == mRearDisplayState.getIdentifier()) {
showRearDisplayEducationalOverlayLocked(request);
} else {
mOverrideRequestController.addRequest(request);
@@ -899,6 +911,28 @@
}
/**
+ * Determines if the system should show an educational dialog before entering rear display mode
+ * @param hasControlDeviceStatePermission If the app has the CONTROL_DEVICE_STATE permission, we
+ * don't need to show the overlay
+ * @param requestingRdmOuterDefault True if the system is requesting
+ * PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT
+ * @param isDeviceClosed True if the device is closed (folded) when the request was made
+ */
+ @VisibleForTesting
+ static boolean shouldShowRdmEduDialog(boolean hasControlDeviceStatePermission,
+ boolean requestingRdmOuterDefault, boolean isDeviceClosed) {
+ if (hasControlDeviceStatePermission) {
+ return false;
+ }
+
+ if (requestingRdmOuterDefault) {
+ return isDeviceClosed;
+ } else {
+ return true;
+ }
+ }
+
+ /**
* If we get a request to enter rear display mode, we need to display an educational
* overlay to let the user know what will happen. This calls into the
* {@link StatusBarManagerInternal} to notify SystemUI to display the educational dialog.
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 5c62995..452dc5f 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -47,6 +47,7 @@
import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.ROOT_UID;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
+import static android.provider.Settings.Secure.MIRROR_BUILT_IN_DISPLAY;
import static android.provider.Settings.Secure.RESOLUTION_MODE_FULL;
import static android.provider.Settings.Secure.RESOLUTION_MODE_HIGH;
import static android.provider.Settings.Secure.RESOLUTION_MODE_UNKNOWN;
@@ -71,6 +72,7 @@
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -534,6 +536,8 @@
private final boolean mExtraDisplayEventLogging;
private final String mExtraDisplayLoggingPackageName;
+ private boolean mMirrorBuiltInDisplay;
+
private final BroadcastReceiver mIdleModeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -782,7 +786,11 @@
}
dpc.onSwitchUser(newUserId, userSerial, newBrightness);
});
- handleSettingsChange();
+ handleMinimalPostProcessingAllowedSettingChange();
+
+ if (mFlags.isDisplayContentModeManagementEnabled()) {
+ updateMirrorBuiltInDisplaySettingLocked();
+ }
}
}
@@ -825,12 +833,15 @@
// relevant configuration should be in place.
recordTopInsetLocked(mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY));
- updateSettingsLocked();
+ updateMinimalPostProcessingAllowedSettingLocked();
updateUserDisabledHdrTypesFromSettingsLocked();
updateUserPreferredDisplayModeSettingsLocked();
if (mIsHdrOutputControlEnabled) {
updateHdrConversionModeSettingsLocked();
}
+ if (mFlags.isDisplayContentModeManagementEnabled()) {
+ updateMirrorBuiltInDisplaySettingLocked();
+ }
}
mDisplayModeDirector.setDesiredDisplayModeSpecsListener(
@@ -1180,27 +1191,61 @@
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(
Settings.Secure.MINIMAL_POST_PROCESSING_ALLOWED), false, this);
+
+ if (mFlags.isDisplayContentModeManagementEnabled()) {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(
+ MIRROR_BUILT_IN_DISPLAY), false, this, UserHandle.USER_ALL);
+ }
}
@Override
public void onChange(boolean selfChange, Uri uri) {
- handleSettingsChange();
+ if (Settings.Secure.getUriFor(
+ Settings.Secure.MINIMAL_POST_PROCESSING_ALLOWED).equals(uri)) {
+ handleMinimalPostProcessingAllowedSettingChange();
+ return;
+ }
+
+ if (Settings.Secure.getUriFor(MIRROR_BUILT_IN_DISPLAY).equals(uri)) {
+ if (mFlags.isDisplayContentModeManagementEnabled()) {
+ updateMirrorBuiltInDisplaySettingLocked();
+ }
+ return;
+ }
}
}
- private void handleSettingsChange() {
+ private void handleMinimalPostProcessingAllowedSettingChange() {
synchronized (mSyncRoot) {
- updateSettingsLocked();
+ updateMinimalPostProcessingAllowedSettingLocked();
scheduleTraversalLocked(false);
}
}
- private void updateSettingsLocked() {
+ private void updateMinimalPostProcessingAllowedSettingLocked() {
setMinimalPostProcessingAllowed(Settings.Secure.getIntForUser(
mContext.getContentResolver(), Settings.Secure.MINIMAL_POST_PROCESSING_ALLOWED,
1, UserHandle.USER_CURRENT) != 0);
}
+ private void updateMirrorBuiltInDisplaySettingLocked() {
+ if (!mFlags.isDisplayContentModeManagementEnabled()) {
+ Slog.e(TAG, "MirrorBuiltInDisplay setting shouldn't be updated when the flag is off.");
+ return;
+ }
+
+ synchronized (mSyncRoot) {
+ ContentResolver resolver = mContext.getContentResolver();
+ final boolean mirrorBuiltInDisplay = Settings.Secure.getIntForUser(resolver,
+ MIRROR_BUILT_IN_DISPLAY, 0, UserHandle.USER_CURRENT) != 0;
+ if (mMirrorBuiltInDisplay == mirrorBuiltInDisplay) {
+ return;
+ }
+ mMirrorBuiltInDisplay = mirrorBuiltInDisplay;
+ }
+ }
+
private void restoreResolutionFromBackup() {
int savedMode = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.SCREEN_RESOLUTION_MODE,
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index 440a271..860be20 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -32,6 +32,7 @@
import android.provider.DeviceConfigInterface;
import android.util.IndentingPrintWriter;
import android.util.Spline;
+import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
@@ -58,6 +59,7 @@
private final DeviceConfigParameterProvider mDeviceConfigParameterProvider;
private final Handler mHandler;
private final LightSensorController mLightSensorController;
+ private int mDisplayState = Display.STATE_OFF;
private final ClamperChangeListener mClamperChangeListenerExternal;
private final Executor mExecutor;
@@ -149,16 +151,13 @@
public DisplayBrightnessState clamp(DisplayBrightnessState displayBrightnessState,
DisplayManagerInternal.DisplayPowerRequest request,
float brightnessValue, boolean slowChange, int displayState) {
+ mDisplayState = displayState;
DisplayBrightnessState.Builder builder = DisplayBrightnessState.Builder.from(
displayBrightnessState);
builder.setIsSlowChange(slowChange);
builder.setBrightness(brightnessValue);
- if (displayState != STATE_ON) {
- mLightSensorController.stop();
- } else {
- adjustLightSensorSubscription();
- }
+ adjustLightSensorSubscription();
for (int i = 0; i < mModifiers.size(); i++) {
mModifiers.get(i).apply(request, builder);
@@ -230,7 +229,8 @@
}
private void adjustLightSensorSubscription() {
- if (mModifiers.stream().anyMatch(BrightnessStateModifier::shouldListenToLightSensor)) {
+ if (mDisplayState == STATE_ON && mModifiers.stream()
+ .anyMatch(BrightnessStateModifier::shouldListenToLightSensor)) {
mLightSensorController.restart();
} else {
mLightSensorController.stop();
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 45106f5..585fc44 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -256,6 +256,10 @@
Flags.FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS,
Flags::displayListenerPerformanceImprovements
);
+ private final FlagState mEnableDisplayContentModeManagementFlagState = new FlagState(
+ Flags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT,
+ Flags::enableDisplayContentModeManagement
+ );
private final FlagState mSubscribeGranularDisplayEvents = new FlagState(
Flags.FLAG_SUBSCRIBE_GRANULAR_DISPLAY_EVENTS,
@@ -556,6 +560,10 @@
return mDisplayListenerPerformanceImprovementsFlagState.isEnabled();
}
+ public boolean isDisplayContentModeManagementEnabled() {
+ return mEnableDisplayContentModeManagementFlagState.isEnabled();
+ }
+
/**
* @return {@code true} if the flag for subscribing to granular display events is enabled
*/
@@ -618,6 +626,7 @@
pw.println(" " + mEnablePluginManagerFlagState);
pw.println(" " + mDisplayListenerPerformanceImprovementsFlagState);
pw.println(" " + mSubscribeGranularDisplayEvents);
+ pw.println(" " + mEnableDisplayContentModeManagementFlagState);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/plugin/PluginEventStorage.java b/services/core/java/com/android/server/display/plugin/PluginEventStorage.java
new file mode 100644
index 0000000..c58ba55
--- /dev/null
+++ b/services/core/java/com/android/server/display/plugin/PluginEventStorage.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.plugin;
+
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.util.RingBuffer;
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+class PluginEventStorage {
+ private static final long TIME_FRAME_LENGTH = 60_000;
+ private static final long MIN_EVENT_DELAY = 500;
+ private static final int MAX_TIME_FRAMES = 10;
+ // not thread safe
+ private static final SimpleDateFormat sDateFormat = new SimpleDateFormat(
+ "MM-dd HH:mm:ss.SSS", Locale.US);
+
+ RingBuffer<TimeFrame> mEvents = new RingBuffer<>(
+ TimeFrame::new, TimeFrame[]::new, MAX_TIME_FRAMES);
+
+ private final Map<PluginType<?>, Long> mEventTimes = new HashMap<>();
+ private long mTimeFrameStart = 0;
+ private final Map<PluginType<?>, EventCounter> mCounters = new HashMap<>();
+
+ <T> void onValueUpdated(PluginType<T> type) {
+ long eventTime = System.currentTimeMillis();
+ if (eventTime - TIME_FRAME_LENGTH > mTimeFrameStart) { // event is in next TimeFrame
+ closeCurrentTimeFrame();
+ mTimeFrameStart = eventTime;
+ }
+ updateCurrentTimeFrame(type, eventTime);
+ }
+
+ private void closeCurrentTimeFrame() {
+ if (!mCounters.isEmpty()) {
+ mEvents.append(new TimeFrame(
+ mTimeFrameStart, mTimeFrameStart + TIME_FRAME_LENGTH, mCounters));
+ mCounters.clear();
+ }
+ }
+
+ private <T> void updateCurrentTimeFrame(PluginType<T> type, long eventTime) {
+ EventCounter counter = mCounters.get(type);
+ long previousTimestamp = mEventTimes.getOrDefault(type, 0L);
+ if (counter == null) {
+ counter = new EventCounter();
+ mCounters.put(type, counter);
+ }
+ counter.increase(eventTime, previousTimestamp);
+ mEventTimes.put(type, eventTime);
+ }
+
+ List<TimeFrame> getTimeFrames() {
+ List<TimeFrame> timeFrames = new ArrayList<>(Arrays.stream(mEvents.toArray()).toList());
+ timeFrames.add(new TimeFrame(
+ mTimeFrameStart, System.currentTimeMillis(), mCounters));
+ return timeFrames;
+ }
+
+ static class TimeFrame {
+ private final long mStart;
+ private final long mEnd;
+ private final Map<PluginType<?>, EventCounter> mCounters;
+
+ private TimeFrame() {
+ this(0, 0, Map.of());
+ }
+
+ private TimeFrame(long start, long end, Map<PluginType<?>, EventCounter> counters) {
+ mStart = start;
+ mEnd = end;
+ mCounters = new HashMap<>(counters);
+ }
+
+ @SuppressWarnings("JavaUtilDate")
+ void dump(PrintWriter pw) {
+ pw.append("TimeFrame:[")
+ .append(sDateFormat.format(new Date(mStart)))
+ .append(" - ")
+ .append(sDateFormat.format(new Date(mEnd)))
+ .println("]:");
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ if (mCounters.isEmpty()) {
+ ipw.println("NO EVENTS");
+ } else {
+ for (Map.Entry<PluginType<?>, EventCounter> entry: mCounters.entrySet()) {
+ ipw.append(entry.getKey().mName).append(" -> {");
+ entry.getValue().dump(ipw);
+ ipw.println("}");
+ }
+ }
+ }
+ }
+
+ private static class EventCounter {
+ private int mEventCounter = 0;
+ private int mFastEventCounter = 0;
+
+ private void increase(long timestamp, long previousTimestamp) {
+ mEventCounter++;
+ if (timestamp - previousTimestamp < MIN_EVENT_DELAY) {
+ mFastEventCounter++;
+ }
+ }
+
+ private void dump(PrintWriter pw) {
+ pw.append("Count:").append(String.valueOf(mEventCounter))
+ .append("; Fast:").append(String.valueOf(mFastEventCounter));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/plugin/PluginStorage.java b/services/core/java/com/android/server/display/plugin/PluginStorage.java
index 2bcea77..dd3415f 100644
--- a/services/core/java/com/android/server/display/plugin/PluginStorage.java
+++ b/services/core/java/com/android/server/display/plugin/PluginStorage.java
@@ -25,6 +25,7 @@
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -39,6 +40,8 @@
private final Map<PluginType<?>, Object> mValues = new HashMap<>();
@GuardedBy("mLock")
private final Map<PluginType<?>, ListenersContainer<?>> mListeners = new HashMap<>();
+ @GuardedBy("mLock")
+ private final PluginEventStorage mPluginEventStorage = new PluginEventStorage();
/**
* Updates value in storage and forwards it to corresponding listeners.
@@ -50,6 +53,7 @@
Set<PluginManager.PluginChangeListener<T>> localListeners;
synchronized (mLock) {
mValues.put(type, value);
+ mPluginEventStorage.onValueUpdated(type);
ListenersContainer<T> container = getListenersContainerForTypeLocked(type);
localListeners = new LinkedHashSet<>(container.mListeners);
}
@@ -91,13 +95,19 @@
Map<PluginType<?>, Object> localValues;
@SuppressWarnings("rawtypes")
Map<PluginType, Set> localListeners = new HashMap<>();
+ List<PluginEventStorage.TimeFrame> timeFrames;
synchronized (mLock) {
+ timeFrames = mPluginEventStorage.getTimeFrames();
localValues = new HashMap<>(mValues);
mListeners.forEach((type, container) -> localListeners.put(type, container.mListeners));
}
pw.println("PluginStorage:");
pw.println("values=" + localValues);
pw.println("listeners=" + localListeners);
+ pw.println("PluginEventStorage:");
+ for (PluginEventStorage.TimeFrame timeFrame: timeFrames) {
+ timeFrame.dump(pw);
+ }
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/input/InputDataStore.java b/services/core/java/com/android/server/input/InputDataStore.java
new file mode 100644
index 0000000..e8f21fe
--- /dev/null
+++ b/services/core/java/com/android/server/input/InputDataStore.java
@@ -0,0 +1,344 @@
+/*
+ * 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.
+ */
+
+package com.android.server.input;
+
+import android.hardware.input.AppLaunchData;
+import android.hardware.input.InputGestureData;
+import android.os.Environment;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages persistent state recorded by the input manager service as a set of XML files.
+ * Caller must acquire lock on the data store before accessing it.
+ */
+public final class InputDataStore {
+ private static final String TAG = "InputDataStore";
+
+ private static final String INPUT_MANAGER_DIRECTORY = "input";
+
+ private static final String TAG_ROOT = "root";
+
+ private static final String TAG_INPUT_GESTURE_LIST = "input_gesture_list";
+ private static final String TAG_INPUT_GESTURE = "input_gesture";
+ private static final String TAG_KEY_TRIGGER = "key_trigger";
+ private static final String TAG_TOUCHPAD_TRIGGER = "touchpad_trigger";
+ private static final String TAG_APP_LAUNCH_DATA = "app_launch_data";
+
+ private static final String ATTR_KEY_TRIGGER_KEYCODE = "keycode";
+ private static final String ATTR_KEY_TRIGGER_MODIFIER_STATE = "modifiers";
+ private static final String ATTR_KEY_GESTURE_TYPE = "key_gesture_type";
+ private static final String ATTR_TOUCHPAD_TRIGGER_GESTURE_TYPE = "touchpad_gesture_type";
+ private static final String ATTR_APP_LAUNCH_DATA_CATEGORY = "category";
+ private static final String ATTR_APP_LAUNCH_DATA_ROLE = "role";
+ private static final String ATTR_APP_LAUNCH_DATA_PACKAGE_NAME = "package_name";
+ private static final String ATTR_APP_LAUNCH_DATA_CLASS_NAME = "class_name";
+
+ private final FileInjector mInputGestureFileInjector;
+
+ public InputDataStore() {
+ this(new FileInjector("input_gestures.xml"));
+ }
+
+ public InputDataStore(final FileInjector inputGestureFileInjector) {
+ mInputGestureFileInjector = inputGestureFileInjector;
+ }
+
+ /**
+ * Reads from the local disk storage the list of customized input gestures.
+ *
+ * @param userId The user id to fetch the gestures for.
+ * @return List of {@link InputGestureData} which the user previously customized.
+ */
+ public List<InputGestureData> loadInputGestures(int userId) {
+ List<InputGestureData> inputGestureDataList;
+ try {
+ final InputStream inputStream = mInputGestureFileInjector.openRead(userId);
+ inputGestureDataList = readInputGesturesXml(inputStream, false);
+ inputStream.close();
+ } catch (IOException exception) {
+ // In case we are unable to read from the file on disk or another IO operation error,
+ // fail gracefully.
+ Slog.e(TAG, "Failed to read from " + mInputGestureFileInjector.getAtomicFileForUserId(
+ userId), exception);
+ return List.of();
+ } catch (Exception exception) {
+ // In the case of any other exception, we want it to bubble up as this would be due
+ // to malformed trusted XML data.
+ throw new RuntimeException(
+ "Failed to read from " + mInputGestureFileInjector.getAtomicFileForUserId(
+ userId), exception);
+ }
+ return inputGestureDataList;
+ }
+
+ /**
+ * Writes to the local disk storage the list of customized input gestures provided as a param.
+ *
+ * @param userId The user id to store the {@link InputGestureData} list under.
+ * @param inputGestureDataList The list of custom input gestures for the given {@code userId}.
+ */
+ public void saveInputGestures(int userId, List<InputGestureData> inputGestureDataList) {
+ FileOutputStream outputStream = null;
+ try {
+ outputStream = mInputGestureFileInjector.startWrite(userId);
+ writeInputGestureXml(outputStream, false, inputGestureDataList);
+ mInputGestureFileInjector.finishWrite(userId, outputStream, true);
+ } catch (IOException e) {
+ Slog.e(TAG,
+ "Failed to write to file " + mInputGestureFileInjector.getAtomicFileForUserId(
+ userId), e);
+ mInputGestureFileInjector.finishWrite(userId, outputStream, false);
+ }
+ }
+
+ @VisibleForTesting
+ List<InputGestureData> readInputGesturesXml(InputStream stream, boolean utf8Encoded)
+ throws XmlPullParserException, IOException {
+ List<InputGestureData> inputGestureDataList = new ArrayList<>();
+ TypedXmlPullParser parser;
+ if (utf8Encoded) {
+ parser = Xml.newFastPullParser();
+ parser.setInput(stream, StandardCharsets.UTF_8.name());
+ } else {
+ parser = Xml.resolvePullParser(stream);
+ }
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ final String tag = parser.getName();
+ if (TAG_ROOT.equals(tag)) {
+ continue;
+ }
+
+ if (TAG_INPUT_GESTURE_LIST.equals(tag)) {
+ inputGestureDataList.addAll(readInputGestureListFromXml(parser));
+ }
+ }
+ return inputGestureDataList;
+ }
+
+ private InputGestureData readInputGestureFromXml(TypedXmlPullParser parser)
+ throws XmlPullParserException, IOException, IllegalArgumentException {
+ InputGestureData.Builder builder = new InputGestureData.Builder();
+ builder.setKeyGestureType(parser.getAttributeInt(null, ATTR_KEY_GESTURE_TYPE));
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ // If the parser has left the initial scope when it was called, break out.
+ if (outerDepth > parser.getDepth()) {
+ throw new RuntimeException(
+ "Parser has left the initial scope of the tag that was being parsed on "
+ + "line number: "
+ + parser.getLineNumber());
+ }
+
+ // If the parser has reached the closing tag for the Input Gesture, break out.
+ if (type == XmlPullParser.END_TAG && parser.getName().equals(TAG_INPUT_GESTURE)) {
+ break;
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ final String tag = parser.getName();
+ if (TAG_KEY_TRIGGER.equals(tag)) {
+ builder.setTrigger(InputGestureData.createKeyTrigger(
+ parser.getAttributeInt(null, ATTR_KEY_TRIGGER_KEYCODE),
+ parser.getAttributeInt(null, ATTR_KEY_TRIGGER_MODIFIER_STATE)));
+ } else if (TAG_TOUCHPAD_TRIGGER.equals(tag)) {
+ builder.setTrigger(InputGestureData.createTouchpadTrigger(
+ parser.getAttributeInt(null, ATTR_TOUCHPAD_TRIGGER_GESTURE_TYPE)));
+ } else if (TAG_APP_LAUNCH_DATA.equals(tag)) {
+ final String roleValue = parser.getAttributeValue(null, ATTR_APP_LAUNCH_DATA_ROLE);
+ final String categoryValue = parser.getAttributeValue(null,
+ ATTR_APP_LAUNCH_DATA_CATEGORY);
+ final String classNameValue = parser.getAttributeValue(null,
+ ATTR_APP_LAUNCH_DATA_CLASS_NAME);
+ final String packageNameValue = parser.getAttributeValue(null,
+ ATTR_APP_LAUNCH_DATA_PACKAGE_NAME);
+ final AppLaunchData appLaunchData = AppLaunchData.createLaunchData(categoryValue,
+ roleValue, packageNameValue, classNameValue);
+ if (appLaunchData != null) {
+ builder.setAppLaunchData(appLaunchData);
+ }
+ }
+ }
+ return builder.build();
+ }
+
+ private List<InputGestureData> readInputGestureListFromXml(TypedXmlPullParser parser) throws
+ XmlPullParserException, IOException {
+ List<InputGestureData> inputGestureDataList = new ArrayList<>();
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ // If the parser has left the initial scope when it was called, break out.
+ if (outerDepth > parser.getDepth()) {
+ throw new RuntimeException(
+ "Parser has left the initial scope of the tag that was being parsed on "
+ + "line number: "
+ + parser.getLineNumber());
+ }
+
+ // If the parser has reached the closing tag for the Input Gesture List, break out.
+ if (type == XmlPullParser.END_TAG && parser.getName().equals(TAG_INPUT_GESTURE_LIST)) {
+ break;
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ final String tag = parser.getName();
+ if (TAG_INPUT_GESTURE.equals(tag)) {
+ try {
+ inputGestureDataList.add(readInputGestureFromXml(parser));
+ } catch (IllegalArgumentException exception) {
+ Slog.w(TAG, "Invalid parameters for input gesture data: ", exception);
+ continue;
+ }
+ }
+ }
+ return inputGestureDataList;
+ }
+
+ @VisibleForTesting
+ void writeInputGestureXml(OutputStream stream, boolean utf8Encoded,
+ List<InputGestureData> inputGestureDataList) throws IOException {
+ final TypedXmlSerializer serializer;
+ if (utf8Encoded) {
+ serializer = Xml.newFastSerializer();
+ serializer.setOutput(stream, StandardCharsets.UTF_8.name());
+ } else {
+ serializer = Xml.resolveSerializer(stream);
+ }
+
+ serializer.startDocument(null, true);
+ serializer.startTag(null, TAG_ROOT);
+ writeInputGestureListToXml(serializer, inputGestureDataList);
+ serializer.endTag(null, TAG_ROOT);
+ serializer.endDocument();
+ }
+
+ private void writeInputGestureToXml(TypedXmlSerializer serializer,
+ InputGestureData inputGestureData) throws IOException {
+ serializer.startTag(null, TAG_INPUT_GESTURE);
+ serializer.attributeInt(null, ATTR_KEY_GESTURE_TYPE,
+ inputGestureData.getAction().keyGestureType());
+
+ final InputGestureData.Trigger trigger = inputGestureData.getTrigger();
+ if (trigger instanceof InputGestureData.KeyTrigger keyTrigger) {
+ serializer.startTag(null, TAG_KEY_TRIGGER);
+ serializer.attributeInt(null, ATTR_KEY_TRIGGER_KEYCODE, keyTrigger.getKeycode());
+ serializer.attributeInt(null, ATTR_KEY_TRIGGER_MODIFIER_STATE,
+ keyTrigger.getModifierState());
+ serializer.endTag(null, TAG_KEY_TRIGGER);
+ } else if (trigger instanceof InputGestureData.TouchpadTrigger touchpadTrigger) {
+ serializer.startTag(null, TAG_TOUCHPAD_TRIGGER);
+ serializer.attributeInt(null, ATTR_TOUCHPAD_TRIGGER_GESTURE_TYPE,
+ touchpadTrigger.getTouchpadGestureType());
+ serializer.endTag(null, TAG_TOUCHPAD_TRIGGER);
+ }
+
+ if (inputGestureData.getAction().appLaunchData() != null) {
+ serializer.startTag(null, TAG_APP_LAUNCH_DATA);
+ final AppLaunchData appLaunchData = inputGestureData.getAction().appLaunchData();
+ if (appLaunchData instanceof AppLaunchData.RoleData roleData) {
+ serializer.attribute(null, ATTR_APP_LAUNCH_DATA_ROLE, roleData.getRole());
+ } else if (appLaunchData
+ instanceof AppLaunchData.CategoryData categoryData) {
+ serializer.attribute(null, ATTR_APP_LAUNCH_DATA_CATEGORY,
+ categoryData.getCategory());
+ } else if (appLaunchData instanceof AppLaunchData.ComponentData componentData) {
+ serializer.attribute(null, ATTR_APP_LAUNCH_DATA_PACKAGE_NAME,
+ componentData.getPackageName());
+ serializer.attribute(null, ATTR_APP_LAUNCH_DATA_CLASS_NAME,
+ componentData.getClassName());
+ }
+ serializer.endTag(null, TAG_APP_LAUNCH_DATA);
+ }
+
+ serializer.endTag(null, TAG_INPUT_GESTURE);
+ }
+
+ private void writeInputGestureListToXml(TypedXmlSerializer serializer,
+ List<InputGestureData> inputGestureDataList) throws IOException {
+ serializer.startTag(null, TAG_INPUT_GESTURE_LIST);
+ for (final InputGestureData inputGestureData : inputGestureDataList) {
+ writeInputGestureToXml(serializer, inputGestureData);
+ }
+ serializer.endTag(null, TAG_INPUT_GESTURE_LIST);
+ }
+
+ @VisibleForTesting
+ static class FileInjector {
+ private final SparseArray<AtomicFile> mAtomicFileMap = new SparseArray<>();
+ private final String mFileName;
+
+ FileInjector(String fileName) {
+ mFileName = fileName;
+ }
+
+ InputStream openRead(int userId) throws FileNotFoundException {
+ return getAtomicFileForUserId(userId).openRead();
+ }
+
+ FileOutputStream startWrite(int userId) throws IOException {
+ return getAtomicFileForUserId(userId).startWrite();
+ }
+
+ void finishWrite(int userId, FileOutputStream os, boolean success) {
+ if (success) {
+ getAtomicFileForUserId(userId).finishWrite(os);
+ } else {
+ getAtomicFileForUserId(userId).failWrite(os);
+ }
+ }
+
+ AtomicFile getAtomicFileForUserId(int userId) {
+ if (!mAtomicFileMap.contains(userId)) {
+ mAtomicFileMap.put(userId, new AtomicFile(new File(
+ Environment.buildPath(Environment.getDataSystemDeDirectory(userId),
+ INPUT_MANAGER_DIRECTORY), mFileName)));
+ }
+ return mAtomicFileMap.get(userId);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index 265e453..bc44fed 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.graphics.PointF;
+import android.hardware.display.DisplayTopology;
import android.hardware.display.DisplayViewport;
import android.hardware.input.KeyGestureEvent;
import android.os.IBinder;
@@ -47,6 +48,12 @@
public abstract void setDisplayViewports(List<DisplayViewport> viewports);
/**
+ * Called by {@link com.android.server.display.DisplayManagerService} to inform InputManager
+ * about changes in the displays topology.
+ */
+ public abstract void setDisplayTopology(DisplayTopology topology);
+
+ /**
* Called by the power manager to tell the input manager whether it should start
* watching for wake events on given displays.
*
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 4454dd4..aee5e7f 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -51,6 +51,7 @@
import android.hardware.SensorPrivacyManager.Sensors;
import android.hardware.SensorPrivacyManagerInternal;
import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayTopology;
import android.hardware.display.DisplayViewport;
import android.hardware.input.AidlInputGestureData;
import android.hardware.input.HostUsiVersion;
@@ -181,6 +182,7 @@
private static final int MSG_RELOAD_DEVICE_ALIASES = 2;
private static final int MSG_DELIVER_TABLET_MODE_CHANGED = 3;
private static final int MSG_CURRENT_USER_CHANGED = 4;
+ private static final int MSG_SYSTEM_READY = 5;
private static final int DEFAULT_VIBRATION_MAGNITUDE = 192;
private static final AdditionalDisplayInputProperties
@@ -351,6 +353,9 @@
// Manages loading PointerIcons
private final PointerIconCache mPointerIconCache;
+ // Manages storage and retrieval of input data.
+ private final InputDataStore mInputDataStore;
+
// Maximum number of milliseconds to wait for input event injection.
private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
@@ -500,7 +505,9 @@
injector.getUEventManager());
mKeyboardBacklightController = injector.getKeyboardBacklightController(mNative);
mStickyModifierStateController = new StickyModifierStateController();
- mKeyGestureController = new KeyGestureController(mContext, injector.getLooper());
+ mInputDataStore = new InputDataStore();
+ mKeyGestureController = new KeyGestureController(mContext, injector.getLooper(),
+ mInputDataStore);
mKeyboardLedController = new KeyboardLedController(mContext, injector.getLooper(),
mNative);
mKeyRemapper = new KeyRemapper(mContext, mNative, mDataStore, injector.getLooper());
@@ -564,6 +571,14 @@
Watchdog.getInstance().addMonitor(this);
}
+ private void onBootPhase(int phase) {
+ // On ActivityManager thread, shift to handler to avoid blocking other system services in
+ // this boot phase.
+ if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ mHandler.sendEmptyMessage(MSG_SYSTEM_READY);
+ }
+ }
+
// TODO(BT) Pass in parameter for bluetooth system
public void systemRunning() {
if (DEBUG) {
@@ -646,6 +661,10 @@
mNative.setPointerDisplayId(mWindowManagerCallbacks.getPointerDisplayId());
}
+ private void setDisplayTopologyInternal(DisplayTopology topology) {
+ mNative.setDisplayTopology(topology.getGraph());
+ }
+
/**
* Gets the current state of a key or button by key code.
* @param deviceId The input device id, or -1 to consult all devices.
@@ -3220,6 +3239,9 @@
case MSG_CURRENT_USER_CHANGED:
handleCurrentUserChanged((int) msg.obj);
break;
+ case MSG_SYSTEM_READY:
+ systemRunning();
+ break;
}
}
}
@@ -3424,10 +3446,7 @@
@Override
public void onBootPhase(int phase) {
- // Called on ActivityManager thread.
- if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
- mService.systemRunning();
- }
+ mService.onBootPhase(phase);
}
@Override
@@ -3447,6 +3466,11 @@
}
@Override
+ public void setDisplayTopology(DisplayTopology topology) {
+ setDisplayTopologyInternal(topology);
+ }
+
+ @Override
public void setDisplayInteractivities(SparseBooleanArray displayInteractivities) {
boolean globallyInteractive = false;
ArraySet<Integer> nonInteractiveDisplays = new ArraySet<>();
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
index 55d2de2..99c01ce 100644
--- a/services/core/java/com/android/server/input/KeyGestureController.java
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -92,6 +92,8 @@
| KeyEvent.META_SHIFT_ON;
private static final int MSG_NOTIFY_KEY_GESTURE_EVENT = 1;
+ private static final int MSG_PERSIST_CUSTOM_GESTURES = 2;
+ private static final int MSG_LOAD_CUSTOM_GESTURES = 3;
// must match: config_settingsKeyBehavior in config.xml
private static final int SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY = 0;
@@ -116,6 +118,8 @@
private final SettingsObserver mSettingsObserver;
private final AppLaunchShortcutManager mAppLaunchShortcutManager;
private final InputGestureManager mInputGestureManager;
+ @GuardedBy("mInputDataStore")
+ private final InputDataStore mInputDataStore;
private static final Object mUserLock = new Object();
@UserIdInt
@GuardedBy("mUserLock")
@@ -155,7 +159,7 @@
/** Currently fully consumed key codes per device */
private final SparseArray<Set<Integer>> mConsumedKeysForDevice = new SparseArray<>();
- KeyGestureController(Context context, Looper looper) {
+ KeyGestureController(Context context, Looper looper, InputDataStore inputDataStore) {
mContext = context;
mHandler = new Handler(looper, this::handleMessage);
mSystemPid = Process.myPid();
@@ -175,6 +179,7 @@
mSettingsObserver = new SettingsObserver(mHandler);
mAppLaunchShortcutManager = new AppLaunchShortcutManager(mContext);
mInputGestureManager = new InputGestureManager(mContext);
+ mInputDataStore = inputDataStore;
initBehaviors();
initKeyCombinationRules();
}
@@ -434,6 +439,13 @@
mSettingsObserver.observe();
mAppLaunchShortcutManager.systemRunning();
mInputGestureManager.systemRunning();
+
+ int userId;
+ synchronized (mUserLock) {
+ userId = mCurrentUserId;
+ }
+ // Load the system user's input gestures.
+ mHandler.obtainMessage(MSG_LOAD_CUSTOM_GESTURES, userId).sendToTarget();
}
public boolean interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
@@ -955,6 +967,7 @@
synchronized (mUserLock) {
mCurrentUserId = userId;
}
+ mHandler.obtainMessage(MSG_LOAD_CUSTOM_GESTURES, userId).sendToTarget();
}
@MainThread
@@ -995,6 +1008,17 @@
AidlKeyGestureEvent event = (AidlKeyGestureEvent) msg.obj;
notifyKeyGestureEvent(event);
break;
+ case MSG_PERSIST_CUSTOM_GESTURES: {
+ final int userId = (Integer) msg.obj;
+ persistInputGestures(userId);
+ break;
+ }
+ case MSG_LOAD_CUSTOM_GESTURES: {
+ final int userId = (Integer) msg.obj;
+ loadInputGestures(userId);
+ break;
+ }
+
}
return true;
}
@@ -1040,22 +1064,31 @@
@InputManager.CustomInputGestureResult
public int addCustomInputGesture(@UserIdInt int userId,
@NonNull AidlInputGestureData inputGestureData) {
- return mInputGestureManager.addCustomInputGesture(userId,
+ final int result = mInputGestureManager.addCustomInputGesture(userId,
new InputGestureData(inputGestureData));
+ if (result == InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS) {
+ mHandler.obtainMessage(MSG_PERSIST_CUSTOM_GESTURES, userId).sendToTarget();
+ }
+ return result;
}
@BinderThread
@InputManager.CustomInputGestureResult
public int removeCustomInputGesture(@UserIdInt int userId,
@NonNull AidlInputGestureData inputGestureData) {
- return mInputGestureManager.removeCustomInputGesture(userId,
+ final int result = mInputGestureManager.removeCustomInputGesture(userId,
new InputGestureData(inputGestureData));
+ if (result == InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS) {
+ mHandler.obtainMessage(MSG_PERSIST_CUSTOM_GESTURES, userId).sendToTarget();
+ }
+ return result;
}
@BinderThread
public void removeAllCustomInputGestures(@UserIdInt int userId,
@Nullable InputGestureData.Filter filter) {
mInputGestureManager.removeAllCustomInputGestures(userId, filter);
+ mHandler.obtainMessage(MSG_PERSIST_CUSTOM_GESTURES, userId).sendToTarget();
}
@BinderThread
@@ -1166,6 +1199,26 @@
}
}
+ private void persistInputGestures(int userId) {
+ synchronized (mInputDataStore) {
+ final List<InputGestureData> inputGestureDataList =
+ mInputGestureManager.getCustomInputGestures(userId,
+ null);
+ mInputDataStore.saveInputGestures(userId, inputGestureDataList);
+ }
+ }
+
+ private void loadInputGestures(int userId) {
+ synchronized (mInputDataStore) {
+ mInputGestureManager.removeAllCustomInputGestures(userId, null);
+ final List<InputGestureData> inputGestureDataList = mInputDataStore.loadInputGestures(
+ userId);
+ for (final InputGestureData inputGestureData : inputGestureDataList) {
+ mInputGestureManager.addCustomInputGesture(userId, inputGestureData);
+ }
+ }
+ }
+
// A record of a registered key gesture event listener from one process.
private class KeyGestureHandlerRecord implements IBinder.DeathRecipient {
public final int mPid;
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index 935f0ff..c72f7c0 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.display.DisplayTopologyGraph;
import android.hardware.display.DisplayViewport;
import android.hardware.input.InputSensorInfo;
import android.hardware.lights.Light;
@@ -42,6 +43,8 @@
void setDisplayViewports(DisplayViewport[] viewports);
+ void setDisplayTopology(DisplayTopologyGraph topologyGraph);
+
int getScanCodeState(int deviceId, int sourceMask, int scanCode);
int getKeyCodeState(int deviceId, int sourceMask, int keyCode);
@@ -323,6 +326,9 @@
public native void setDisplayViewports(DisplayViewport[] viewports);
@Override
+ public native void setDisplayTopology(DisplayTopologyGraph topologyGraph);
+
+ @Override
public native int getScanCodeState(int deviceId, int sourceMask, int scanCode);
@Override
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
index 53389ca..ef73463 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -20,10 +20,18 @@
import android.hardware.contexthub.EndpointInfo;
import android.hardware.contexthub.HubEndpointInfo;
import android.hardware.contexthub.HubMessage;
-import android.hardware.contexthub.HubServiceInfo;
import android.hardware.contexthub.IContextHubEndpoint;
import android.hardware.contexthub.IContextHubEndpointCallback;
import android.hardware.location.IContextHubTransactionCallback;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* A class that represents a broker for the endpoint registered by the client app. This class
@@ -31,7 +39,8 @@
*
* @hide
*/
-public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub {
+public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
+ implements IBinder.DeathRecipient {
private static final String TAG = "ContextHubEndpointBroker";
/** The context of the service. */
@@ -52,6 +61,23 @@
/** The remote callback interface for this endpoint. */
private final IContextHubEndpointCallback mContextHubEndpointCallback;
+ /** True if this endpoint is registered with the service. */
+ private AtomicBoolean mIsRegistered = new AtomicBoolean(true);
+
+ private final Object mOpenSessionLock = new Object();
+
+ /** The set of session IDs that are pending remote acceptance */
+ @GuardedBy("mOpenSessionLock")
+ private final Set<Integer> mPendingSessionIds = new HashSet<>();
+
+ /** The set of session IDs that are actively enabled by this endpoint */
+ @GuardedBy("mOpenSessionLock")
+ private final Set<Integer> mActiveSessionIds = new HashSet<>();
+
+ /** The set of session IDs that are actively enabled by the remote endpoint */
+ @GuardedBy("mOpenSessionLock")
+ private final Set<Integer> mActiveRemoteSessionIds = new HashSet<>();
+
/* package */ ContextHubEndpointBroker(
Context context,
IContextHubWrapper contextHubProxy,
@@ -72,27 +98,78 @@
}
@Override
- public int openSession(HubEndpointInfo destination, HubServiceInfo serviceInfo) {
- // TODO(b/378487799): Implement this
- return 0;
+ public int openSession(HubEndpointInfo destination, String serviceDescriptor)
+ throws RemoteException {
+ ContextHubServiceUtil.checkPermissions(mContext);
+ if (!mIsRegistered.get()) throw new IllegalStateException("Endpoint is not registered");
+ int sessionId = mEndpointManager.reserveSessionId();
+ EndpointInfo halEndpointInfo = ContextHubServiceUtil.convertHalEndpointInfo(destination);
+
+ synchronized (mOpenSessionLock) {
+ try {
+ mPendingSessionIds.add(sessionId);
+ mContextHubProxy.openEndpointSession(
+ sessionId,
+ halEndpointInfo.id,
+ mHalEndpointInfo.id,
+ serviceDescriptor);
+ } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
+ Log.e(TAG, "Exception while calling HAL openEndpointSession", e);
+ mPendingSessionIds.remove(sessionId);
+ mEndpointManager.returnSessionId(sessionId);
+ throw e;
+ }
+
+ return sessionId;
+ }
}
@Override
- public void closeSession(int sessionId, int reason) {
- // TODO(b/378487799): Implement this
-
- }
-
- @Override
- public void openSessionRequestComplete(int sessionId) {
- // TODO(b/378487799): Implement this
-
+ public void closeSession(int sessionId, int reason) throws RemoteException {
+ ContextHubServiceUtil.checkPermissions(mContext);
+ if (!mIsRegistered.get()) throw new IllegalStateException("Endpoint is not registered");
+ try {
+ mContextHubProxy.closeEndpointSession(sessionId, (byte) reason);
+ } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
+ Log.e(TAG, "Exception while calling HAL closeEndpointSession", e);
+ throw e;
+ }
}
@Override
public void unregister() {
- // TODO(b/378487799): Implement this
+ ContextHubServiceUtil.checkPermissions(mContext);
+ mIsRegistered.set(false);
+ try {
+ mContextHubProxy.unregisterEndpoint(mHalEndpointInfo);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling HAL unregisterEndpoint", e);
+ }
+ synchronized (mOpenSessionLock) {
+ for (int id : mPendingSessionIds) {
+ mEndpointManager.returnSessionId(id);
+ }
+ for (int id : mActiveSessionIds) {
+ mEndpointManager.returnSessionId(id);
+ }
+ mPendingSessionIds.clear();
+ mActiveSessionIds.clear();
+ mActiveRemoteSessionIds.clear();
+ }
+ mEndpointManager.unregisterEndpoint(mEndpointInfo.getIdentifier().getEndpoint());
+ }
+ @Override
+ public void openSessionRequestComplete(int sessionId) {
+ ContextHubServiceUtil.checkPermissions(mContext);
+ synchronized (mOpenSessionLock) {
+ try {
+ mContextHubProxy.endpointSessionOpenComplete(sessionId);
+ mActiveRemoteSessionIds.add(sessionId);
+ } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
+ Log.e(TAG, "Exception while calling endpointSessionOpenComplete", e);
+ }
+ }
}
@Override
@@ -105,4 +182,67 @@
public void sendMessageDeliveryStatus(int sessionId, int messageSeqNumber, byte errorCode) {
// TODO(b/381102453): Implement this
}
+
+ /** Invoked when the underlying binder of this broker has died at the client process. */
+ @Override
+ public void binderDied() {
+ if (mIsRegistered.get()) {
+ unregister();
+ }
+ }
+
+ /* package */ void attachDeathRecipient() throws RemoteException {
+ if (mContextHubEndpointCallback != null) {
+ mContextHubEndpointCallback.asBinder().linkToDeath(this, 0 /* flags */);
+ }
+ }
+
+ /* package */ void onEndpointSessionOpenRequest(
+ int sessionId, HubEndpointInfo initiator, String serviceDescriptor) {
+ if (mContextHubEndpointCallback != null) {
+ try {
+ mContextHubEndpointCallback.onSessionOpenRequest(
+ sessionId, initiator, serviceDescriptor);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling onSessionOpenRequest", e);
+ }
+ }
+ }
+
+ /* package */ void onCloseEndpointSession(int sessionId, byte reason) {
+ synchronized (mOpenSessionLock) {
+ mPendingSessionIds.remove(sessionId);
+ mActiveSessionIds.remove(sessionId);
+ mActiveRemoteSessionIds.remove(sessionId);
+ }
+ if (mContextHubEndpointCallback != null) {
+ try {
+ mContextHubEndpointCallback.onSessionClosed(sessionId, reason);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling onSessionClosed", e);
+ }
+ }
+ }
+
+ /* package */ void onEndpointSessionOpenComplete(int sessionId) {
+ synchronized (mOpenSessionLock) {
+ mPendingSessionIds.remove(sessionId);
+ mActiveSessionIds.add(sessionId);
+ }
+ if (mContextHubEndpointCallback != null) {
+ try {
+ mContextHubEndpointCallback.onSessionOpenComplete(sessionId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling onSessionClosed", e);
+ }
+ }
+ }
+
+ /* package */ boolean hasSessionId(int sessionId) {
+ synchronized (mOpenSessionLock) {
+ return mPendingSessionIds.contains(sessionId)
+ || mActiveSessionIds.contains(sessionId)
+ || mActiveRemoteSessionIds.contains(sessionId);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
index 54ce74f..155a92a 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
@@ -22,11 +22,15 @@
import android.hardware.contexthub.IContextHubEndpoint;
import android.hardware.contexthub.IContextHubEndpointCallback;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -34,30 +38,93 @@
*
* @hide
*/
-/* package */ class ContextHubEndpointManager {
+/* package */ class ContextHubEndpointManager
+ implements ContextHubHalEndpointCallback.IEndpointSessionCallback {
private static final String TAG = "ContextHubEndpointManager";
/** The hub ID of the Context Hub Service. */
private static final long SERVICE_HUB_ID = 0x416e64726f696400L;
+ /** The range of session IDs to use for endpoints */
+ private static final int SERVICE_SESSION_RANGE = 1024;
+
+ /** The length of the array that should be returned by HAL requestSessionIdRange */
+ private static final int SERVICE_SESSION_RANGE_LENGTH = 2;
+
/** The context of the service. */
private final Context mContext;
/** The proxy to talk to the Context Hub. */
private final IContextHubWrapper mContextHubProxy;
+ private final HubInfoRegistry mHubInfoRegistry;
+
/** A map of endpoint IDs to brokers currently registered. */
private final Map<Long, ContextHubEndpointBroker> mEndpointMap = new ConcurrentHashMap<>();
/** Variables for managing endpoint ID creation */
private final Object mEndpointLock = new Object();
+ /**
+ * The next available endpoint ID to register. Per EndpointId.aidl definition, dynamic
+ * endpoint IDs must have the left-most bit as 1, and the values 0/-1 are invalid.
+ */
@GuardedBy("mEndpointLock")
- private long mNextEndpointId = 0;
+ private long mNextEndpointId = -2;
- /* package */ ContextHubEndpointManager(Context context, IContextHubWrapper contextHubProxy) {
+ /** The minimum session ID reservable by endpoints (retrieved from HAL) */
+ private final int mMinSessionId;
+
+ /** The minimum session ID reservable by endpoints (retrieved from HAL) */
+ private final int mMaxSessionId;
+
+ /** Variables for managing session ID creation */
+ private final Object mSessionIdLock = new Object();
+
+ /** A set of session IDs that have been reserved by an endpoint. */
+ @GuardedBy("mSessionIdLock")
+ private final Set<Integer> mReservedSessionIds =
+ Collections.newSetFromMap(new HashMap<Integer, Boolean>());
+
+ @GuardedBy("mSessionIdLock")
+ private int mNextSessionId = 0;
+
+ /** Initialized to true if all initialization in the constructor succeeds. */
+ private final boolean mSessionIdsValid;
+
+ /* package */ ContextHubEndpointManager(
+ Context context, IContextHubWrapper contextHubProxy, HubInfoRegistry hubInfoRegistry) {
mContext = context;
mContextHubProxy = contextHubProxy;
+ mHubInfoRegistry = hubInfoRegistry;
+ int[] range = null;
+ try {
+ range = mContextHubProxy.requestSessionIdRange(SERVICE_SESSION_RANGE);
+ if (range != null && range.length < SERVICE_SESSION_RANGE_LENGTH) {
+ Log.e(TAG, "Invalid session ID range: range array size = " + range.length);
+ range = null;
+ }
+ } catch (RemoteException | IllegalArgumentException | ServiceSpecificException e) {
+ Log.e(TAG, "Exception while calling HAL requestSessionIdRange", e);
+ }
+
+ if (range == null) {
+ mMinSessionId = -1;
+ mMaxSessionId = -1;
+ mSessionIdsValid = false;
+ } else {
+ mMinSessionId = range[0];
+ mMaxSessionId = range[1];
+ if (!isSessionIdRangeValid(mMinSessionId, mMaxSessionId)) {
+ Log.e(
+ TAG,
+ "Invalid session ID range: max=" + mMaxSessionId + " min=" + mMinSessionId);
+ mSessionIdsValid = false;
+ } else {
+ mNextSessionId = mMinSessionId;
+ mSessionIdsValid = true;
+ }
+ }
}
/**
@@ -71,6 +138,9 @@
/* package */ IContextHubEndpoint registerEndpoint(
HubEndpointInfo pendingEndpointInfo, IContextHubEndpointCallback callback)
throws RemoteException {
+ if (!mSessionIdsValid) {
+ throw new IllegalStateException("ContextHubEndpointManager failed to initialize");
+ }
ContextHubEndpointBroker broker;
long endpointId = getNewEndpointId();
EndpointInfo halEndpointInfo =
@@ -91,21 +161,142 @@
callback);
mEndpointMap.put(endpointId, broker);
- // TODO(b/378487799): Add death recipient
+ try {
+ broker.attachDeathRecipient();
+ } catch (RemoteException e) {
+ // The client process has died, so we close the connection and return null
+ Log.e(TAG, "Failed to attach death recipient to client", e);
+ broker.unregister();
+ return null;
+ }
Log.d(TAG, "Registered endpoint with ID = " + endpointId);
return IContextHubEndpoint.Stub.asInterface(broker);
}
/**
- * @return an available endpoint ID
+ * Reserves an available session ID for an endpoint.
+ *
+ * @throws IllegalStateException if no session ID was available.
+ * @return The reserved session ID.
*/
+ /* package */ int reserveSessionId() {
+ int id = -1;
+ synchronized (mSessionIdLock) {
+ final int maxCapacity = mMaxSessionId - mMinSessionId + 1;
+ if (mReservedSessionIds.size() >= maxCapacity) {
+ throw new IllegalStateException("Too many sessions reserved");
+ }
+
+ id = mNextSessionId;
+ for (int i = mMinSessionId; i <= mMaxSessionId; i++) {
+ if (!mReservedSessionIds.contains(id)) {
+ mNextSessionId = (id == mMaxSessionId) ? mMinSessionId : id + 1;
+ break;
+ }
+
+ id = (id == mMaxSessionId) ? mMinSessionId : id + 1;
+ }
+
+ mReservedSessionIds.add(id);
+ }
+ return id;
+ }
+
+ /** Returns a previously reserved session ID through {@link #reserveSessionId()}. */
+ /* package */ void returnSessionId(int sessionId) {
+ synchronized (mSessionIdLock) {
+ mReservedSessionIds.remove(sessionId);
+ }
+ }
+
+ /**
+ * Unregisters an endpoint given its ID.
+ *
+ * @param endpointId The ID of the endpoint to unregister.
+ */
+ /* package */ void unregisterEndpoint(long endpointId) {
+ mEndpointMap.remove(endpointId);
+ }
+
+ @Override
+ public void onEndpointSessionOpenRequest(
+ int sessionId,
+ HubEndpointInfo.HubEndpointIdentifier destination,
+ HubEndpointInfo.HubEndpointIdentifier initiator,
+ String serviceDescriptor) {
+ if (destination.getHub() != SERVICE_HUB_ID) {
+ Log.e(
+ TAG,
+ "onEndpointSessionOpenRequest: invalid destination hub ID: "
+ + destination.getHub());
+ return;
+ }
+ ContextHubEndpointBroker broker = mEndpointMap.get(destination.getEndpoint());
+ if (broker == null) {
+ Log.e(
+ TAG,
+ "onEndpointSessionOpenRequest: unknown destination endpoint ID: "
+ + destination.getEndpoint());
+ return;
+ }
+ HubEndpointInfo initiatorInfo = mHubInfoRegistry.getEndpointInfo(initiator);
+ if (initiatorInfo == null) {
+ Log.e(
+ TAG,
+ "onEndpointSessionOpenRequest: unknown initiator endpoint ID: "
+ + initiator.getEndpoint());
+ return;
+ }
+ broker.onEndpointSessionOpenRequest(sessionId, initiatorInfo, serviceDescriptor);
+ }
+
+ @Override
+ public void onCloseEndpointSession(int sessionId, byte reason) {
+ boolean callbackInvoked = false;
+ for (ContextHubEndpointBroker broker : mEndpointMap.values()) {
+ if (broker.hasSessionId(sessionId)) {
+ broker.onCloseEndpointSession(sessionId, reason);
+ callbackInvoked = true;
+ break;
+ }
+ }
+
+ if (!callbackInvoked) {
+ Log.w(TAG, "onCloseEndpointSession: unknown session ID " + sessionId);
+ }
+ }
+
+ @Override
+ public void onEndpointSessionOpenComplete(int sessionId) {
+ boolean callbackInvoked = false;
+ for (ContextHubEndpointBroker broker : mEndpointMap.values()) {
+ if (broker.hasSessionId(sessionId)) {
+ broker.onEndpointSessionOpenComplete(sessionId);
+ callbackInvoked = true;
+ break;
+ }
+ }
+
+ if (!callbackInvoked) {
+ Log.w(TAG, "onEndpointSessionOpenComplete: unknown session ID " + sessionId);
+ }
+ }
+
+ /** @return an available endpoint ID */
private long getNewEndpointId() {
synchronized (mEndpointLock) {
- if (mNextEndpointId == Long.MAX_VALUE) {
+ if (mNextEndpointId >= 0) {
throw new IllegalStateException("Too many endpoints connected");
}
- return mNextEndpointId++;
+ return mNextEndpointId--;
}
}
+
+ /**
+ * @return true if the provided session ID range is valid
+ */
+ private boolean isSessionIdRangeValid(int minId, int maxId) {
+ return (minId <= maxId) && (minId >= 0) && (maxId >= 0);
+ }
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubHalEndpointCallback.java b/services/core/java/com/android/server/location/contexthub/ContextHubHalEndpointCallback.java
index c05f7a0..9d52c6a 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubHalEndpointCallback.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubHalEndpointCallback.java
@@ -26,6 +26,7 @@
public class ContextHubHalEndpointCallback
extends android.hardware.contexthub.IEndpointCallback.Stub {
private final IEndpointLifecycleCallback mEndpointLifecycleCallback;
+ private final IEndpointSessionCallback mEndpointSessionCallback;
/** Interface for listening for endpoint start and stop events. */
public interface IEndpointLifecycleCallback {
@@ -36,8 +37,27 @@
void onEndpointStopped(HubEndpointInfo.HubEndpointIdentifier[] endpointIds, byte reason);
}
- ContextHubHalEndpointCallback(IEndpointLifecycleCallback endpointLifecycleCallback) {
+ /** Interface for listening for endpoint session events. */
+ public interface IEndpointSessionCallback {
+ /** Called when an endpoint session open is requested by the HAL. */
+ void onEndpointSessionOpenRequest(
+ int sessionId,
+ HubEndpointInfo.HubEndpointIdentifier destinationId,
+ HubEndpointInfo.HubEndpointIdentifier initiatorId,
+ String serviceDescriptor);
+
+ /** Called when a endpoint close session is completed. */
+ void onCloseEndpointSession(int sessionId, byte reason);
+
+ /** Called when a requested endpoint open session is completed */
+ void onEndpointSessionOpenComplete(int sessionId);
+ }
+
+ ContextHubHalEndpointCallback(
+ IEndpointLifecycleCallback endpointLifecycleCallback,
+ IEndpointSessionCallback endpointSessionCallback) {
mEndpointLifecycleCallback = endpointLifecycleCallback;
+ mEndpointSessionCallback = endpointSessionCallback;
}
@Override
@@ -48,7 +68,7 @@
}
HubEndpointInfo[] endpointInfos = new HubEndpointInfo[halEndpointInfos.length];
for (int i = 0; i < halEndpointInfos.length; i++) {
- endpointInfos[i++] = new HubEndpointInfo(halEndpointInfos[i]);
+ endpointInfos[i] = new HubEndpointInfo(halEndpointInfos[i]);
}
mEndpointLifecycleCallback.onEndpointStarted(endpointInfos);
}
@@ -72,14 +92,23 @@
@Override
public void onEndpointSessionOpenRequest(
- int i, EndpointId endpointId, EndpointId endpointId1, String s)
- throws RemoteException {}
+ int i, EndpointId destination, EndpointId initiator, String s) throws RemoteException {
+ HubEndpointInfo.HubEndpointIdentifier destinationId =
+ new HubEndpointInfo.HubEndpointIdentifier(destination.hubId, destination.id);
+ HubEndpointInfo.HubEndpointIdentifier initiatorId =
+ new HubEndpointInfo.HubEndpointIdentifier(initiator.hubId, initiator.id);
+ mEndpointSessionCallback.onEndpointSessionOpenRequest(i, destinationId, initiatorId, s);
+ }
@Override
- public void onCloseEndpointSession(int i, byte b) throws RemoteException {}
+ public void onCloseEndpointSession(int i, byte b) throws RemoteException {
+ mEndpointSessionCallback.onCloseEndpointSession(i, b);
+ }
@Override
- public void onEndpointSessionOpenComplete(int i) throws RemoteException {}
+ public void onEndpointSessionOpenComplete(int i) throws RemoteException {
+ mEndpointSessionCallback.onEndpointSessionOpenComplete(i);
+ }
@Override
public int getInterfaceVersion() throws RemoteException {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 0d926b9..d916eda 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -333,7 +333,8 @@
HubInfoRegistry registry;
try {
registry = new HubInfoRegistry(mContextHubWrapper);
- mEndpointManager = new ContextHubEndpointManager(mContext, mContextHubWrapper);
+ mEndpointManager =
+ new ContextHubEndpointManager(mContext, mContextHubWrapper, registry);
Log.i(TAG, "Enabling generic offload API");
} catch (UnsupportedOperationException e) {
mEndpointManager = null;
@@ -533,7 +534,7 @@
}
try {
mContextHubWrapper.registerEndpointCallback(
- new ContextHubHalEndpointCallback(mHubInfoRegistry));
+ new ContextHubHalEndpointCallback(mHubInfoRegistry, mEndpointManager));
} catch (RemoteException | UnsupportedOperationException e) {
Log.e(TAG, "Exception while registering IEndpointCallback", e);
}
@@ -797,7 +798,7 @@
throws RemoteException {
super.registerEndpoint_enforcePermission();
if (mEndpointManager == null) {
- Log.e(TAG, "ContextHubService.registerEndpoint: endpoint manager failed to initialize");
+ Log.e(TAG, "Endpoint manager failed to initialize");
throw new UnsupportedOperationException("Endpoint registration is not supported");
}
return mEndpointManager.registerEndpoint(pendingHubEndpointInfo, callback);
@@ -808,7 +809,8 @@
public void registerEndpointDiscoveryCallbackId(
long endpointId, IContextHubEndpointDiscoveryCallback callback) throws RemoteException {
super.registerEndpointDiscoveryCallbackId_enforcePermission();
- // TODO(b/375487784): Implement this
+ checkEndpointDiscoveryPreconditions();
+ mHubInfoRegistry.registerEndpointDiscoveryCallback(endpointId, callback);
}
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@@ -817,7 +819,8 @@
String serviceDescriptor, IContextHubEndpointDiscoveryCallback callback)
throws RemoteException {
super.registerEndpointDiscoveryCallbackDescriptor_enforcePermission();
- // TODO(b/375487784): Implement this
+ checkEndpointDiscoveryPreconditions();
+ mHubInfoRegistry.registerEndpointDiscoveryCallback(serviceDescriptor, callback);
}
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@@ -825,7 +828,15 @@
public void unregisterEndpointDiscoveryCallback(IContextHubEndpointDiscoveryCallback callback)
throws RemoteException {
super.unregisterEndpointDiscoveryCallback_enforcePermission();
- // TODO(b/375487784): Implement this
+ checkEndpointDiscoveryPreconditions();
+ mHubInfoRegistry.unregisterEndpointDiscoveryCallback(callback);
+ }
+
+ private void checkEndpointDiscoveryPreconditions() {
+ if (mHubInfoRegistry == null) {
+ Log.e(TAG, "Hub endpoint registry failed to initialize");
+ throw new UnsupportedOperationException("Endpoint discovery is not supported");
+ }
}
/**
diff --git a/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java b/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java
index d2b2331..6f5f191 100644
--- a/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java
+++ b/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java
@@ -18,7 +18,9 @@
import android.hardware.contexthub.HubEndpointInfo;
import android.hardware.contexthub.HubServiceInfo;
+import android.hardware.contexthub.IContextHubEndpointDiscoveryCallback;
import android.hardware.location.HubInfo;
+import android.os.DeadObjectException;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
@@ -29,6 +31,9 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.BiConsumer;
class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycleCallback {
private static final String TAG = "HubInfoRegistry";
@@ -43,6 +48,56 @@
private final ArrayMap<HubEndpointInfo.HubEndpointIdentifier, HubEndpointInfo>
mHubEndpointInfos = new ArrayMap<>();
+ /**
+ * A wrapper class that is used to store arguments to
+ * ContextHubManager.registerEndpointCallback.
+ */
+ private static class DiscoveryCallback {
+ private final IContextHubEndpointDiscoveryCallback mCallback;
+ private final Optional<Long> mEndpointId;
+ private final Optional<String> mServiceDescriptor;
+
+ DiscoveryCallback(IContextHubEndpointDiscoveryCallback callback, long endpointId) {
+ mCallback = callback;
+ mEndpointId = Optional.of(endpointId);
+ mServiceDescriptor = Optional.empty();
+ }
+
+ DiscoveryCallback(IContextHubEndpointDiscoveryCallback callback, String serviceDescriptor) {
+ mCallback = callback;
+ mEndpointId = Optional.empty();
+ mServiceDescriptor = Optional.of(serviceDescriptor);
+ }
+
+ public IContextHubEndpointDiscoveryCallback getCallback() {
+ return mCallback;
+ }
+
+ /**
+ * @param info The hub endpoint info to check
+ * @return true if info matches
+ */
+ public boolean isMatch(HubEndpointInfo info) {
+ if (mEndpointId.isPresent()) {
+ return mEndpointId.get() == info.getIdentifier().getEndpoint();
+ }
+ if (mServiceDescriptor.isPresent()) {
+ for (HubServiceInfo serviceInfo : info.getServiceInfoCollection()) {
+ if (mServiceDescriptor.get().equals(serviceInfo.getServiceDescriptor())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ /* The list of discovery callbacks registered with the service */
+ @GuardedBy("mCallbackLock")
+ private final List<DiscoveryCallback> mEndpointDiscoveryCallbacks = new ArrayList<>();
+
+ private final Object mCallbackLock = new Object();
+
HubInfoRegistry(IContextHubWrapper contextHubWrapper) {
mContextHubWrapper = contextHubWrapper;
refreshCachedHubs();
@@ -87,6 +142,12 @@
}
}
+ public HubEndpointInfo getEndpointInfo(HubEndpointInfo.HubEndpointIdentifier id) {
+ synchronized (mLock) {
+ return mHubEndpointInfos.get(id);
+ }
+ }
+
/** Invoked when HAL restarts */
public void onHalRestart() {
synchronized (mLock) {
@@ -103,16 +164,50 @@
mHubEndpointInfos.put(endpointInfo.getIdentifier(), endpointInfo);
}
}
+
+ invokeForMatchingEndpoints(
+ endpointInfos,
+ (cb, infoList) -> {
+ try {
+ cb.onEndpointsStarted(infoList);
+ } catch (RemoteException e) {
+ if (e instanceof DeadObjectException) {
+ Log.w(TAG, "onEndpointStarted: callback died, unregistering");
+ unregisterEndpointDiscoveryCallback(cb);
+ } else {
+ Log.e(TAG, "Exception while calling onEndpointsStarted", e);
+ }
+ }
+ });
}
@Override
public void onEndpointStopped(
HubEndpointInfo.HubEndpointIdentifier[] endpointIds, byte reason) {
+ ArrayList<HubEndpointInfo> removedInfoList = new ArrayList<>();
synchronized (mLock) {
for (HubEndpointInfo.HubEndpointIdentifier endpointId : endpointIds) {
- mHubEndpointInfos.remove(endpointId);
+ HubEndpointInfo info = mHubEndpointInfos.remove(endpointId);
+ if (info != null) {
+ removedInfoList.add(info);
+ }
}
}
+
+ invokeForMatchingEndpoints(
+ removedInfoList.toArray(new HubEndpointInfo[removedInfoList.size()]),
+ (cb, infoList) -> {
+ try {
+ cb.onEndpointsStopped(infoList, reason);
+ } catch (RemoteException e) {
+ if (e instanceof DeadObjectException) {
+ Log.w(TAG, "onEndpointStopped: callback died, unregistering");
+ unregisterEndpointDiscoveryCallback(cb);
+ } else {
+ Log.e(TAG, "Exception while calling onEndpointsStopped", e);
+ }
+ }
+ });
}
/** Return a list of {@link HubEndpointInfo} that represents endpoints with the matching id. */
@@ -145,6 +240,77 @@
return searchResult;
}
+ /* package */
+ void registerEndpointDiscoveryCallback(
+ long endpointId, IContextHubEndpointDiscoveryCallback callback) {
+ Objects.requireNonNull(callback, "callback cannot be null");
+ synchronized (mCallbackLock) {
+ checkCallbackAlreadyRegistered(callback);
+ mEndpointDiscoveryCallbacks.add(new DiscoveryCallback(callback, endpointId));
+ }
+ }
+
+ /* package */
+ void registerEndpointDiscoveryCallback(
+ String serviceDescriptor, IContextHubEndpointDiscoveryCallback callback) {
+ Objects.requireNonNull(callback, "callback cannot be null");
+ synchronized (mCallbackLock) {
+ checkCallbackAlreadyRegistered(callback);
+ mEndpointDiscoveryCallbacks.add(new DiscoveryCallback(callback, serviceDescriptor));
+ }
+ }
+
+ /* package */
+ void unregisterEndpointDiscoveryCallback(IContextHubEndpointDiscoveryCallback callback) {
+ Objects.requireNonNull(callback, "callback cannot be null");
+ synchronized (mCallbackLock) {
+ for (DiscoveryCallback discoveryCallback : mEndpointDiscoveryCallbacks) {
+ if (discoveryCallback.getCallback().asBinder() == callback.asBinder()) {
+ mEndpointDiscoveryCallbacks.remove(discoveryCallback);
+ break;
+ }
+ }
+ }
+ }
+
+ private void checkCallbackAlreadyRegistered(
+ IContextHubEndpointDiscoveryCallback callback) {
+ synchronized (mCallbackLock) {
+ for (DiscoveryCallback discoveryCallback : mEndpointDiscoveryCallbacks) {
+ if (discoveryCallback.mCallback.asBinder() == callback.asBinder()) {
+ throw new IllegalArgumentException("Callback is already registered");
+ }
+ }
+ }
+ }
+
+ /**
+ * Iterates through all registered discovery callbacks and invokes a given callback for those
+ * that match the endpoints the callback is targeted for.
+ *
+ * @param endpointInfos The list of endpoint infos to check for a match.
+ * @param consumer The callback to invoke, which consumes the callback object and the list of
+ * matched endpoint infos.
+ */
+ private void invokeForMatchingEndpoints(
+ HubEndpointInfo[] endpointInfos,
+ BiConsumer<IContextHubEndpointDiscoveryCallback, HubEndpointInfo[]> consumer) {
+ synchronized (mCallbackLock) {
+ for (DiscoveryCallback discoveryCallback : mEndpointDiscoveryCallbacks) {
+ ArrayList<HubEndpointInfo> infoList = new ArrayList<>();
+ for (HubEndpointInfo endpointInfo : endpointInfos) {
+ if (discoveryCallback.isMatch(endpointInfo)) {
+ infoList.add(endpointInfo);
+ }
+ }
+
+ consumer.accept(
+ discoveryCallback.getCallback(),
+ infoList.toArray(new HubEndpointInfo[infoList.size()]));
+ }
+ }
+ }
+
void dump(IndentingPrintWriter ipw) {
synchronized (mLock) {
dumpLocked(ipw);
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 14d75b0..6cb9429 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.chre.flags.Flags;
+import android.hardware.contexthub.EndpointId;
import android.hardware.contexthub.HostEndpointInfo;
import android.hardware.contexthub.HubEndpointInfo;
import android.hardware.contexthub.MessageDeliveryStatus;
@@ -243,6 +244,26 @@
public void registerEndpoint(android.hardware.contexthub.EndpointInfo info)
throws RemoteException {}
+ /** Unregisters a previously registered endpoint */
+ public int[] requestSessionIdRange(int size) throws RemoteException {
+ return null;
+ }
+
+ /** Opens an endpoint session between two endpoints */
+ public void openEndpointSession(
+ int sessionId, EndpointId destination, EndpointId initiator, String serviceDescriptor)
+ throws RemoteException {}
+
+ /** Closes a previously opened endpoint */
+ public void closeEndpointSession(int sessionId, byte reason) throws RemoteException {}
+
+ /** Unregisters a previously registered endpoint */
+ public void unregisterEndpoint(android.hardware.contexthub.EndpointInfo info)
+ throws RemoteException {}
+
+ /** Notifies the completion of a session opened by the HAL */
+ public void endpointSessionOpenComplete(int sessionId) throws RemoteException {}
+
/**
* @return True if this version of the Contexthub HAL supports Location setting notifications.
*/
@@ -685,6 +706,57 @@
hub.registerEndpoint(info);
}
+ @Override
+ public int[] requestSessionIdRange(int size) throws RemoteException {
+ android.hardware.contexthub.IContextHub hub = getHub();
+ if (hub == null) {
+ return null;
+ }
+ return hub.requestSessionIdRange(size);
+ }
+
+ @Override
+ public void openEndpointSession(
+ int sessionId,
+ EndpointId destination,
+ EndpointId initiator,
+ String serviceDescriptor)
+ throws RemoteException {
+ android.hardware.contexthub.IContextHub hub = getHub();
+ if (hub == null) {
+ return;
+ }
+ hub.openEndpointSession(sessionId, destination, initiator, serviceDescriptor);
+ }
+
+ @Override
+ public void closeEndpointSession(int sessionId, byte reason) throws RemoteException {
+ android.hardware.contexthub.IContextHub hub = getHub();
+ if (hub == null) {
+ return;
+ }
+ hub.closeEndpointSession(sessionId, reason);
+ }
+
+ @Override
+ public void unregisterEndpoint(android.hardware.contexthub.EndpointInfo info)
+ throws RemoteException {
+ android.hardware.contexthub.IContextHub hub = getHub();
+ if (hub == null) {
+ return;
+ }
+ hub.unregisterEndpoint(info);
+ }
+
+ @Override
+ public void endpointSessionOpenComplete(int sessionId) throws RemoteException {
+ android.hardware.contexthub.IContextHub hub = getHub();
+ if (hub == null) {
+ return;
+ }
+ hub.endpointSessionOpenComplete(sessionId);
+ }
+
public boolean supportsLocationSettingNotifications() {
return true;
}
diff --git a/services/core/java/com/android/server/media/AudioManagerRouteController.java b/services/core/java/com/android/server/media/AudioManagerRouteController.java
index 0f65d1d..2686f2b 100644
--- a/services/core/java/com/android/server/media/AudioManagerRouteController.java
+++ b/services/core/java/com/android/server/media/AudioManagerRouteController.java
@@ -46,6 +46,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.server.media.BluetoothRouteController.NoOpBluetoothRouteController;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -378,7 +379,12 @@
Slog.e(
TAG,
"Could not map this selected device attribute type to an available route: "
- + selectedDeviceAttributesType);
+ + selectedDeviceAttributesType
+ + ". Available types: "
+ + Arrays.toString(
+ Arrays.stream(audioDeviceInfos)
+ .map(AudioDeviceInfo::getType)
+ .toArray()));
// We know mRouteIdToAvailableDeviceRoutes is not empty.
newSelectedRouteHolder = mRouteIdToAvailableDeviceRoutes.values().iterator().next();
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index ab68ed3..abc067d 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -2347,13 +2347,24 @@
if (!Flags.enableRouteVisibilityControlApi()) {
return true;
}
- for (String permission : route.getRequiredPermissions()) {
- if (mContext.checkPermission(permission, mPid, mUid)
- != PackageManager.PERMISSION_GRANTED) {
- return false;
+ List<Set<String>> permissionSets = route.getRequiredPermissions();
+ if (permissionSets.isEmpty()) {
+ return true;
+ }
+ for (Set<String> permissionSet : permissionSets) {
+ boolean hasAllInSet = true;
+ for (String permission : permissionSet) {
+ if (mContext.checkPermission(permission, mPid, mUid)
+ != PackageManager.PERMISSION_GRANTED) {
+ hasAllInSet = false;
+ break;
+ }
+ }
+ if (hasAllInSet) {
+ return true;
}
}
- return true;
+ return false;
}
}
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index 4479e9d..1673b8e 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -30,7 +30,9 @@
import android.media.quality.PictureProfile;
import android.media.quality.PictureProfileHandle;
import android.media.quality.SoundProfile;
+import android.media.quality.SoundProfileHandle;
import android.os.PersistableBundle;
+import android.os.UserHandle;
import android.util.Log;
import com.android.server.SystemService;
@@ -45,8 +47,8 @@
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
-import java.util.stream.Collectors;
import java.util.UUID;
+import java.util.stream.Collectors;
/**
* This service manage picture profile and sound profile for TV setting. Also communicates with the
@@ -80,7 +82,7 @@
private final class BinderService extends IMediaQualityManager.Stub {
@Override
- public PictureProfile createPictureProfile(PictureProfile pp, int userId) {
+ public PictureProfile createPictureProfile(PictureProfile pp, UserHandle user) {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
@@ -99,12 +101,12 @@
}
@Override
- public void updatePictureProfile(String id, PictureProfile pp, int userId) {
+ public void updatePictureProfile(String id, PictureProfile pp, UserHandle user) {
// TODO: implement
}
@Override
- public void removePictureProfile(String id, int userId) {
+ public void removePictureProfile(String id, UserHandle user) {
Long intId = mPictureProfileTempIdMap.inverse().get(id);
if (intId != null) {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
@@ -117,7 +119,8 @@
}
@Override
- public PictureProfile getPictureProfile(int type, String name, int userId) {
+ public PictureProfile getPictureProfile(int type, String name, boolean includeParams,
+ UserHandle user) {
String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
+ BaseParameters.PARAMETER_NAME + " = ?";
String[] selectionArguments = {Integer.toString(type), name};
@@ -143,7 +146,8 @@
}
@Override
- public List<PictureProfile> getPictureProfilesByPackage(String packageName, int userId) {
+ public List<PictureProfile> getPictureProfilesByPackage(
+ String packageName, boolean includeParams, UserHandle user) {
String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
String[] selectionArguments = {packageName};
return getPictureProfilesBasedOnConditions(getAllMediaProfileColumns(), selection,
@@ -151,18 +155,19 @@
}
@Override
- public List<PictureProfile> getAvailablePictureProfiles(int userId) {
+ public List<PictureProfile> getAvailablePictureProfiles(
+ boolean includeParams, UserHandle user) {
return new ArrayList<>();
}
@Override
- public boolean setDefaultPictureProfile(String profileId, int userId) {
+ public boolean setDefaultPictureProfile(String profileId, UserHandle user) {
// TODO: pass the profile ID to MediaQuality HAL when ready.
return false;
}
@Override
- public List<String> getPictureProfilePackageNames(int userId) {
+ public List<String> getPictureProfilePackageNames(UserHandle user) {
String [] column = {BaseParameters.PARAMETER_PACKAGE};
List<PictureProfile> pictureProfiles = getPictureProfilesBasedOnConditions(column,
null, null);
@@ -173,12 +178,17 @@
}
@Override
- public PictureProfileHandle getPictureProfileHandle(String id, int userId) {
- return null;
+ public List<PictureProfileHandle> getPictureProfileHandle(String[] id, UserHandle user) {
+ return new ArrayList<>();
}
@Override
- public SoundProfile createSoundProfile(SoundProfile sp, int userId) {
+ public List<SoundProfileHandle> getSoundProfileHandle(String[] id, UserHandle user) {
+ return new ArrayList<>();
+ }
+
+ @Override
+ public SoundProfile createSoundProfile(SoundProfile sp, UserHandle user) {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
@@ -197,12 +207,12 @@
}
@Override
- public void updateSoundProfile(String id, SoundProfile pp, int userId) {
+ public void updateSoundProfile(String id, SoundProfile pp, UserHandle user) {
// TODO: implement
}
@Override
- public void removeSoundProfile(String id, int userId) {
+ public void removeSoundProfile(String id, UserHandle user) {
Long intId = mSoundProfileTempIdMap.inverse().get(id);
if (intId != null) {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
@@ -215,9 +225,10 @@
}
@Override
- public SoundProfile getSoundProfile(int type, String id, int userId) {
+ public SoundProfile getSoundProfile(int type, String id, boolean includeParams,
+ UserHandle user) {
String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
- + BaseParameters.PARAMETER_NAME + " = ?";
+ + BaseParameters.PARAMETER_ID + " = ?";
String[] selectionArguments = {String.valueOf(type), id};
try (
@@ -241,7 +252,8 @@
}
@Override
- public List<SoundProfile> getSoundProfilesByPackage(String packageName, int userId) {
+ public List<SoundProfile> getSoundProfilesByPackage(
+ String packageName, boolean includeParams, UserHandle user) {
String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
String[] selectionArguments = {packageName};
return getSoundProfilesBasedOnConditions(getAllMediaProfileColumns(), selection,
@@ -249,18 +261,19 @@
}
@Override
- public List<SoundProfile> getAvailableSoundProfiles(int userId) {
+ public List<SoundProfile> getAvailableSoundProfiles(
+ boolean includeParams, UserHandle user) {
return new ArrayList<>();
}
@Override
- public boolean setDefaultSoundProfile(String profileId, int userId) {
+ public boolean setDefaultSoundProfile(String profileId, UserHandle user) {
// TODO: pass the profile ID to MediaQuality HAL when ready.
return false;
}
@Override
- public List<String> getSoundProfilePackageNames(int userId) {
+ public List<String> getSoundProfilePackageNames(UserHandle user) {
String [] column = {BaseParameters.PARAMETER_NAME};
List<SoundProfile> soundProfiles = getSoundProfilesBasedOnConditions(column,
null, null);
@@ -352,7 +365,8 @@
getName(cursor),
getInputId(cursor),
getPackageName(cursor),
- jsonToBundle(getSettingsString(cursor))
+ jsonToBundle(getSettingsString(cursor)),
+ PictureProfileHandle.NONE
);
}
@@ -363,7 +377,8 @@
getName(cursor),
getInputId(cursor),
getPackageName(cursor),
- jsonToBundle(getSettingsString(cursor))
+ jsonToBundle(getSettingsString(cursor)),
+ SoundProfileHandle.NONE
);
}
@@ -448,70 +463,71 @@
}
@Override
- public void setAmbientBacklightSettings(AmbientBacklightSettings settings, int userId) {
+ public void setAmbientBacklightSettings(
+ AmbientBacklightSettings settings, UserHandle user) {
}
@Override
- public void setAmbientBacklightEnabled(boolean enabled, int userId) {
+ public void setAmbientBacklightEnabled(boolean enabled, UserHandle user) {
}
@Override
- public List<ParamCapability> getParamCapabilities(List<String> names, int userId) {
+ public List<ParamCapability> getParamCapabilities(List<String> names, UserHandle user) {
return new ArrayList<>();
}
@Override
- public List<String> getPictureProfileAllowList(int userId) {
+ public List<String> getPictureProfileAllowList(UserHandle user) {
return new ArrayList<>();
}
@Override
- public void setPictureProfileAllowList(List<String> packages, int userId) {
+ public void setPictureProfileAllowList(List<String> packages, UserHandle user) {
}
@Override
- public List<String> getSoundProfileAllowList(int userId) {
+ public List<String> getSoundProfileAllowList(UserHandle user) {
return new ArrayList<>();
}
@Override
- public void setSoundProfileAllowList(List<String> packages, int userId) {
+ public void setSoundProfileAllowList(List<String> packages, UserHandle user) {
}
@Override
- public boolean isSupported(int userId) {
+ public boolean isSupported(UserHandle user) {
return false;
}
@Override
- public void setAutoPictureQualityEnabled(boolean enabled, int userId) {
+ public void setAutoPictureQualityEnabled(boolean enabled, UserHandle user) {
}
@Override
- public boolean isAutoPictureQualityEnabled(int userId) {
+ public boolean isAutoPictureQualityEnabled(UserHandle user) {
return false;
}
@Override
- public void setSuperResolutionEnabled(boolean enabled, int userId) {
+ public void setSuperResolutionEnabled(boolean enabled, UserHandle user) {
}
@Override
- public boolean isSuperResolutionEnabled(int userId) {
+ public boolean isSuperResolutionEnabled(UserHandle user) {
return false;
}
@Override
- public void setAutoSoundQualityEnabled(boolean enabled, int userId) {
+ public void setAutoSoundQualityEnabled(boolean enabled, UserHandle user) {
}
@Override
- public boolean isAutoSoundQualityEnabled(int userId) {
+ public boolean isAutoSoundQualityEnabled(UserHandle user) {
return false;
}
@Override
- public boolean isAmbientBacklightEnabled(int userId) {
+ public boolean isAmbientBacklightEnabled(UserHandle user) {
return false;
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 15af36b..39eea74 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2594,6 +2594,7 @@
intent.setPackage(pkg);
intent.putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, id);
intent.putExtra(EXTRA_AUTOMATIC_ZEN_RULE_STATUS, status);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
getContext().sendBroadcastAsUser(intent, UserHandle.of(userId));
});
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index dc173b1..95aff56 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -112,9 +112,10 @@
import android.util.StatsEvent;
import android.util.proto.ProtoOutputStream;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.FrameworkStatsLog;
@@ -279,6 +280,11 @@
mCallbacks.remove(callback);
}
+ @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+ public List<Callback> getCallbacks() {
+ return mCallbacks;
+ }
+
public void initZenMode() {
if (DEBUG) Log.d(TAG, "initZenMode");
synchronized (mConfigLock) {
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index f79d9ef..65a38ae 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -123,13 +123,6 @@
}
flag {
- name: "use_ipcdatacache_channels"
- namespace: "systemui"
- description: "Adds an IPCDataCache for notification channel/group lookups"
- bug: "331677193"
-}
-
-flag {
name: "use_ssm_user_switch_signal"
namespace: "systemui"
description: "This flag controls which signal is used to handle a user switch system event"
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OWNERS b/services/core/java/com/android/server/ondeviceintelligence/OWNERS
deleted file mode 100644
index 09774f7..0000000
--- a/services/core/java/com/android/server/ondeviceintelligence/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file:/core/java/android/app/ondeviceintelligence/OWNERS
diff --git a/services/core/java/com/android/server/os/instrumentation/DynamicInstrumentationManagerService.java b/services/core/java/com/android/server/os/instrumentation/DynamicInstrumentationManagerService.java
index 8ec7160..871d12e 100644
--- a/services/core/java/com/android/server/os/instrumentation/DynamicInstrumentationManagerService.java
+++ b/services/core/java/com/android/server/os/instrumentation/DynamicInstrumentationManagerService.java
@@ -20,116 +20,100 @@
import static android.content.Context.DYNAMIC_INSTRUMENTATION_SERVICE;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.PermissionManuallyEnforced;
+import android.annotation.RequiresPermission;
+import android.app.ActivityManagerInternal;
import android.content.Context;
+import android.os.RemoteException;
import android.os.instrumentation.ExecutableMethodFileOffsets;
import android.os.instrumentation.IDynamicInstrumentationManager;
+import android.os.instrumentation.IOffsetCallback;
import android.os.instrumentation.MethodDescriptor;
+import android.os.instrumentation.MethodDescriptorParser;
import android.os.instrumentation.TargetProcess;
-import com.android.internal.annotations.VisibleForTesting;
+
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import dalvik.system.VMDebug;
import java.lang.reflect.Method;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+
/**
* System private implementation of the {@link IDynamicInstrumentationManager interface}.
*/
public class DynamicInstrumentationManagerService extends SystemService {
+
+ private ActivityManagerInternal mAmInternal;
+
public DynamicInstrumentationManagerService(@NonNull Context context) {
super(context);
}
@Override
public void onStart() {
+ mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
publishBinderService(DYNAMIC_INSTRUMENTATION_SERVICE, new BinderService());
}
private final class BinderService extends IDynamicInstrumentationManager.Stub {
@Override
@PermissionManuallyEnforced
- public @Nullable ExecutableMethodFileOffsets getExecutableMethodFileOffsets(
- @NonNull TargetProcess targetProcess, @NonNull MethodDescriptor methodDescriptor) {
+ @RequiresPermission(value = android.Manifest.permission.DYNAMIC_INSTRUMENTATION)
+ public void getExecutableMethodFileOffsets(
+ @NonNull TargetProcess targetProcess, @NonNull MethodDescriptor methodDescriptor,
+ @NonNull IOffsetCallback callback) {
if (!com.android.art.flags.Flags.executableMethodFileOffsets()) {
throw new UnsupportedOperationException();
}
getContext().enforceCallingOrSelfPermission(
DYNAMIC_INSTRUMENTATION, "Caller must have DYNAMIC_INSTRUMENTATION permission");
+ Objects.requireNonNull(targetProcess.processName);
- if (targetProcess.processName == null
- || !targetProcess.processName.equals("system_server")) {
- throw new UnsupportedOperationException(
- "system_server is the only supported target process");
+ if (!targetProcess.processName.equals("system_server")) {
+ try {
+ mAmInternal.getExecutableMethodFileOffsets(targetProcess.processName,
+ targetProcess.pid, targetProcess.uid, methodDescriptor,
+ new IOffsetCallback.Stub() {
+ @Override
+ public void onResult(ExecutableMethodFileOffsets result) {
+ try {
+ callback.onResult(result);
+ } catch (RemoteException e) {
+ /* ignore */
+ }
+ }
+ });
+ return;
+ } catch (NoSuchElementException e) {
+ throw new IllegalArgumentException(
+ "The specified app process cannot be found." , e);
+ }
}
- Method method = parseMethodDescriptor(
+ Method method = MethodDescriptorParser.parseMethodDescriptor(
getClass().getClassLoader(), methodDescriptor);
VMDebug.ExecutableMethodFileOffsets location =
VMDebug.getExecutableMethodFileOffsets(method);
- if (location == null) {
- return null;
- }
-
- ExecutableMethodFileOffsets ret = new ExecutableMethodFileOffsets();
- ret.containerPath = location.getContainerPath();
- ret.containerOffset = location.getContainerOffset();
- ret.methodOffset = location.getMethodOffset();
- return ret;
- }
- }
-
- @VisibleForTesting
- static Method parseMethodDescriptor(ClassLoader classLoader,
- @NonNull MethodDescriptor descriptor) {
- try {
- Class<?> javaClass = classLoader.loadClass(descriptor.fullyQualifiedClassName);
- Class<?>[] parameters = new Class[descriptor.fullyQualifiedParameters.length];
- for (int i = 0; i < descriptor.fullyQualifiedParameters.length; i++) {
- String typeName = descriptor.fullyQualifiedParameters[i];
- boolean isArrayType = typeName.endsWith("[]");
- if (isArrayType) {
- typeName = typeName.substring(0, typeName.length() - 2);
+ try {
+ if (location == null) {
+ callback.onResult(null);
+ return;
}
- switch (typeName) {
- case "boolean":
- parameters[i] = isArrayType ? boolean.class.arrayType() : boolean.class;
- break;
- case "byte":
- parameters[i] = isArrayType ? byte.class.arrayType() : byte.class;
- break;
- case "char":
- parameters[i] = isArrayType ? char.class.arrayType() : char.class;
- break;
- case "short":
- parameters[i] = isArrayType ? short.class.arrayType() : short.class;
- break;
- case "int":
- parameters[i] = isArrayType ? int.class.arrayType() : int.class;
- break;
- case "long":
- parameters[i] = isArrayType ? long.class.arrayType() : long.class;
- break;
- case "float":
- parameters[i] = isArrayType ? float.class.arrayType() : float.class;
- break;
- case "double":
- parameters[i] = isArrayType ? double.class.arrayType() : double.class;
- break;
- default:
- parameters[i] = isArrayType ? classLoader.loadClass(typeName).arrayType()
- : classLoader.loadClass(typeName);
- }
- }
- return javaClass.getDeclaredMethod(descriptor.methodName, parameters);
- } catch (ClassNotFoundException | NoSuchMethodException e) {
- throw new IllegalArgumentException(
- "The specified method cannot be found. Is this descriptor valid? "
- + descriptor, e);
+ ExecutableMethodFileOffsets ret = new ExecutableMethodFileOffsets();
+ ret.containerPath = location.getContainerPath();
+ ret.containerOffset = location.getContainerOffset();
+ ret.methodOffset = location.getMethodOffset();
+ callback.onResult(ret);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failed to invoke result callback", e);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index 6d54be8..d78e98b 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -78,6 +78,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.function.BiFunction;
+import java.util.function.Supplier;
/**
* Helper class to send broadcasts for various situations.
@@ -216,7 +217,7 @@
filterExtrasForReceiver, bOptions);
}
- void sendResourcesChangedBroadcast(@NonNull Computer snapshot,
+ void sendResourcesChangedBroadcast(@NonNull Supplier<Computer> snapshotSupplier,
boolean mediaStatus,
boolean replacing,
@NonNull String[] pkgNames,
@@ -236,7 +237,7 @@
null /* targetPkg */, null /* finishedReceiver */, null /* userIds */,
null /* instantUserIds */, null /* broadcastAllowList */,
(callingUid, intentExtras) -> filterExtrasChangedPackageList(
- snapshot, callingUid, intentExtras),
+ snapshotSupplier, callingUid, intentExtras),
null /* bOptions */, null /* requiredPermissions */);
}
@@ -544,7 +545,7 @@
});
}
- void sendPostInstallBroadcasts(@NonNull Computer snapshot,
+ void sendPostInstallBroadcasts(@NonNull Supplier<Computer> snapshotSupplier,
@NonNull InstallRequest request,
@NonNull String packageName,
@NonNull String requiredPermissionControllerPackage,
@@ -567,8 +568,8 @@
final int[] uids = new int[]{request.getRemovedInfo().mUid};
notifyResourcesChanged(
false /* mediaStatus */, true /* replacing */, pkgNames, uids);
- sendResourcesChangedBroadcast(
- snapshot, false /* mediaStatus */, true /* replacing */, pkgNames, uids);
+ sendResourcesChangedBroadcast(snapshotSupplier,
+ false /* mediaStatus */, true /* replacing */, pkgNames, uids);
}
sendPackageRemovedBroadcasts(
request.getRemovedInfo(), packageSender, isKillApp, false /*removedBySystem*/,
@@ -608,6 +609,7 @@
null /* broadcastAllowList */, null);
}
+ final Computer snapshot = snapshotSupplier.get();
// Send installed broadcasts if the package is not a static shared lib.
if (staticSharedLibraryName == null) {
// Send PACKAGE_ADDED broadcast for users that see the package for the first time
@@ -732,7 +734,7 @@
if (!isArchived) {
final String[] pkgNames = new String[]{packageName};
final int[] uids = new int[]{request.getPkg().getUid()};
- sendResourcesChangedBroadcast(snapshot,
+ sendResourcesChangedBroadcast(snapshotSupplier,
true /* mediaStatus */, true /* replacing */, pkgNames, uids);
notifyResourcesChanged(true /* mediaStatus */,
true /* replacing */, pkgNames, uids);
@@ -860,8 +862,8 @@
* access all the packages in the extras.
*/
@Nullable
- private static Bundle filterExtrasChangedPackageList(@NonNull Computer snapshot, int callingUid,
- @NonNull Bundle extras) {
+ private static Bundle filterExtrasChangedPackageList(
+ @NonNull Supplier<Computer> snapshotSupplier, int callingUid, @NonNull Bundle extras) {
if (UserHandle.isCore(callingUid)) {
// see all
return extras;
@@ -873,6 +875,7 @@
final int userId = extras.getInt(
Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(callingUid));
final int[] uids = extras.getIntArray(Intent.EXTRA_CHANGED_UID_LIST);
+ final Computer snapshot = snapshotSupplier.get();
final Pair<String[], int[]> filteredPkgs =
filterPackages(snapshot, pkgs, uids, callingUid, userId);
if (ArrayUtils.isEmpty(filteredPkgs.first)) {
@@ -952,10 +955,12 @@
final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
final SparseArray<int[]> broadcastAllowList =
isInstantApp ? null : snapshot.getVisibilityAllowLists(packageName, userIds);
+ final String[] sharedUserPackages =
+ snapshot.getSharedUserPackagesForPackage(packageName, userId);
mHandler.post(() -> sendPackageChangedBroadcastInternal(
packageName, dontKillApp, componentNames, packageUid, reason, userIds,
instantUserIds, broadcastAllowList, setting.getPkg(),
- snapshot.getSharedUserPackagesForPackage(packageName, userId)));
+ sharedUserPackages));
mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames,
packageUid, reason, userIds, instantUserIds, broadcastAllowList, mHandler);
}
@@ -1120,7 +1125,7 @@
* @param uidList The uids of packages which have suspension changes.
* @param userId The user where packages reside.
*/
- void sendPackagesSuspendedOrUnsuspendedForUser(@NonNull Computer snapshot,
+ void sendPackagesSuspendedOrUnsuspendedForUser(@NonNull Supplier<Computer> snapshotSupplier,
@NonNull String intent,
@NonNull String[] pkgList,
@NonNull int[] uidList,
@@ -1138,7 +1143,7 @@
.toBundle();
BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver =
(callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
- snapshot, callingUid, intentExtras);
+ snapshotSupplier, callingUid, intentExtras);
mHandler.post(() -> sendPackageBroadcast(intent, null /* pkg */,
extras, flags, null /* targetPkg */, null /* finishedReceiver */,
new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */,
@@ -1148,7 +1153,7 @@
null /* instantUserIds */, null /* broadcastAllowList */, filterExtrasForReceiver);
}
- void sendMyPackageSuspendedOrUnsuspended(@NonNull Computer snapshot,
+ void sendMyPackageSuspendedOrUnsuspended(@NonNull Supplier<Computer> snapshotSupplier,
@NonNull String[] affectedPackages,
boolean suspended,
int userId) {
@@ -1163,6 +1168,7 @@
return;
}
final int[] targetUserIds = new int[] {userId};
+ final Computer snapshot = snapshotSupplier.get();
for (String packageName : affectedPackages) {
final Bundle appExtras = suspended
? SuspendPackageHelper.getSuspendedPackageAppExtras(
@@ -1192,7 +1198,7 @@
* @param uidList The uids of packages which have suspension changes.
* @param userId The user where packages reside.
*/
- void sendDistractingPackagesChanged(@NonNull Computer snapshot,
+ void sendDistractingPackagesChanged(@NonNull Supplier<Computer> snapshotSupplier,
@NonNull String[] pkgList,
@NonNull int[] uidList,
int userId,
@@ -1208,11 +1214,11 @@
null /* finishedReceiver */, new int[]{userId}, null /* instantUserIds */,
null /* broadcastAllowList */,
(callingUid, intentExtras) -> filterExtrasChangedPackageList(
- snapshot, callingUid, intentExtras),
+ snapshotSupplier, callingUid, intentExtras),
null /* bOptions */, null /* requiredPermissions */));
}
- void sendResourcesChangedBroadcastAndNotify(@NonNull Computer snapshot,
+ void sendResourcesChangedBroadcastAndNotify(@NonNull Supplier<Computer> snapshotSupplier,
boolean mediaStatus,
boolean replacing,
@NonNull ArrayList<AndroidPackage> packages) {
@@ -1224,7 +1230,7 @@
packageNames[i] = pkg.getPackageName();
packageUids[i] = pkg.getUid();
}
- sendResourcesChangedBroadcast(snapshot, mediaStatus,
+ sendResourcesChangedBroadcast(snapshotSupplier, mediaStatus,
replacing, packageNames, packageUids);
notifyResourcesChanged(mediaStatus, replacing, packageNames, packageUids);
}
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 58b1e49..be2f58d 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -138,7 +138,8 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.modules.utils.TypedXmlSerializer;
-import com.android.server.ondeviceintelligence.OnDeviceIntelligenceManagerInternal;
+import com.android.server.LocalManagerRegistry;
+import com.android.server.ondeviceintelligence.OnDeviceIntelligenceManagerLocal;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.parsing.PackageInfoUtils;
@@ -5851,10 +5852,10 @@
if (isHotword) {
return true;
}
- OnDeviceIntelligenceManagerInternal onDeviceIntelligenceManagerInternal =
- mInjector.getLocalService(OnDeviceIntelligenceManagerInternal.class);
- return onDeviceIntelligenceManagerInternal != null
- && uid == onDeviceIntelligenceManagerInternal.getInferenceServiceUid();
+ OnDeviceIntelligenceManagerLocal onDeviceIntelligenceManagerLocal =
+ LocalManagerRegistry.getManager(OnDeviceIntelligenceManagerLocal.class);
+ return onDeviceIntelligenceManagerLocal != null
+ && uid == onDeviceIntelligenceManagerLocal.getInferenceServiceUid();
}
@Nullable
diff --git a/services/core/java/com/android/server/pm/DistractingPackageHelper.java b/services/core/java/com/android/server/pm/DistractingPackageHelper.java
index c5ec73b..c4e981d 100644
--- a/services/core/java/com/android/server/pm/DistractingPackageHelper.java
+++ b/services/core/java/com/android/server/pm/DistractingPackageHelper.java
@@ -123,7 +123,7 @@
if (!changedPackagesList.isEmpty()) {
final String[] changedPackages = changedPackagesList.toArray(
new String[changedPackagesList.size()]);
- mBroadcastHelper.sendDistractingPackagesChanged(mPm.snapshotComputer(),
+ mBroadcastHelper.sendDistractingPackagesChanged(mPm::snapshotComputer,
changedPackages, changedUids.toArray(), userId, restrictionFlags);
mPm.scheduleWritePackageRestrictions(userId);
}
@@ -198,7 +198,7 @@
if (!changedPackages.isEmpty()) {
final String[] packageArray = changedPackages.toArray(
new String[changedPackages.size()]);
- mBroadcastHelper.sendDistractingPackagesChanged(mPm.snapshotComputer(),
+ mBroadcastHelper.sendDistractingPackagesChanged(mPm::snapshotComputer,
packageArray, changedUids.toArray(), userId, RESTRICTION_NONE);
mPm.scheduleWritePackageRestrictions(userId);
}
diff --git a/services/core/java/com/android/server/pm/InstallDependencyHelper.java b/services/core/java/com/android/server/pm/InstallDependencyHelper.java
index 673a102..c0ddebe 100644
--- a/services/core/java/com/android/server/pm/InstallDependencyHelper.java
+++ b/services/core/java/com/android/server/pm/InstallDependencyHelper.java
@@ -129,14 +129,14 @@
}
}
- void notifySessionComplete(int sessionId, boolean success) {
+ void notifySessionComplete(int sessionId) {
if (DEBUG) {
- Slog.d(TAG, "Session complete for " + sessionId + " result: " + success);
+ Slog.d(TAG, "Session complete for " + sessionId);
}
synchronized (mTrackers) {
List<DependencyInstallTracker> completedTrackers = new ArrayList<>();
for (DependencyInstallTracker tracker: mTrackers) {
- if (!tracker.onSessionComplete(sessionId, success)) {
+ if (!tracker.onSessionComplete(sessionId)) {
completedTrackers.add(tracker);
}
}
@@ -354,7 +354,7 @@
// Don't wait for sessions that finished already
if (sessionInfo == null) {
Binder.withCleanCallingIdentity(() -> {
- notifySessionComplete(sessionId, /*success=*/ true);
+ notifySessionComplete(sessionId);
});
}
}
@@ -459,19 +459,13 @@
*
* Returns true if we still need to continue tracking.
*/
- public boolean onSessionComplete(int sessionId, boolean success) {
+ public boolean onSessionComplete(int sessionId) {
synchronized (this) {
if (!mPendingSessionIds.contains(sessionId)) {
// This had no impact on tracker, so continue tracking
return true;
}
- if (!success) {
- // If one of the dependency fails, the orig session would fail too.
- onError(mCallback, "Failed to install all dependencies");
- return false; // No point in tracking anymore
- }
-
mPendingSessionIds.remove(sessionId);
if (mPendingSessionIds.isEmpty()) {
mCallback.onResult(null);
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 69c6ce8..183ceae 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -3084,7 +3084,7 @@
mPm.mProcessLoggingHandler.invalidateBaseApkHash(request.getPkg().getBaseApkPath());
}
- mBroadcastHelper.sendPostInstallBroadcasts(mPm.snapshotComputer(), request, packageName,
+ mBroadcastHelper.sendPostInstallBroadcasts(mPm::snapshotComputer, request, packageName,
mPm.mRequiredPermissionControllerPackage, mPm.mRequiredVerifierPackages,
mPm.mRequiredInstallerPackage,
/* packageSender= */ mPm, launchedForRestore, killApp, update, archived);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 516b002..e1fcc66 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -2330,8 +2330,7 @@
}
if (Flags.sdkDependencyInstaller()) {
- mInstallDependencyHelper.notifySessionComplete(
- session.sessionId, success);
+ mInstallDependencyHelper.notifySessionComplete(session.sessionId);
}
final File appIconFile = buildAppIconFile(session.sessionId);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 65bb701..fd0a90c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4063,6 +4063,8 @@
mPendingBroadcasts.remove(userId, packageName);
} else {
mPendingBroadcasts.addComponent(userId, packageName, componentName);
+ Trace.instant(Trace.TRACE_TAG_PACKAGE_MANAGER, "setEnabledSetting broadcast: "
+ + componentName + ": " + setting.getEnabledState());
scheduleBroadcastMessage = true;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 9757582..7af39f7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -749,7 +749,7 @@
null /*abiOverride*/, false /*isIncremental*/);
} catch (IOException e) {
logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
- + "; pkg: " + packageName);
+ + "; pkg: " + packageName + "; err: " + e.getMessage());
return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
} finally {
IoUtils.closeQuietly(handle);
diff --git a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
index 4b82de0..ef49f49 100644
--- a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
+++ b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
@@ -34,6 +34,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -196,21 +197,13 @@
private void doNotifyCallbacksByIntent(Intent intent, int userId,
int[] broadcastAllowList, Handler handler) {
- RemoteCallbackList<IRemoteCallback> callbacks;
- synchronized (mLock) {
- callbacks = mCallbacks;
- }
- doNotifyCallbacks(callbacks, intent, userId, broadcastAllowList, handler,
+ doNotifyCallbacks(intent, userId, broadcastAllowList, handler,
null /* filterExtrasFunction */);
}
private void doNotifyCallbacksByAction(String action, String pkg, Bundle extras, int[] userIds,
SparseArray<int[]> broadcastAllowList, Handler handler,
BiFunction<Integer, Bundle, Bundle> filterExtrasFunction) {
- RemoteCallbackList<IRemoteCallback> callbacks;
- synchronized (mLock) {
- callbacks = mCallbacks;
- }
for (int userId : userIds) {
final Intent intent = new Intent(action,
pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null);
@@ -226,48 +219,58 @@
final int[] allowUids =
broadcastAllowList != null ? broadcastAllowList.get(userId) : null;
- doNotifyCallbacks(callbacks, intent, userId, allowUids, handler, filterExtrasFunction);
+ doNotifyCallbacks(intent, userId, allowUids, handler, filterExtrasFunction);
}
}
- private void doNotifyCallbacks(RemoteCallbackList<IRemoteCallback> callbacks,
- Intent intent, int userId, int[] allowUids, Handler handler,
+ private void doNotifyCallbacks(Intent intent, int userId, int[] allowUids, Handler handler,
BiFunction<Integer, Bundle, Bundle> filterExtrasFunction) {
- handler.post(() -> callbacks.broadcast((callback, user) -> {
- RegisterUser registerUser = (RegisterUser) user;
- if ((registerUser.getUserId() != UserHandle.USER_ALL) && (registerUser.getUserId()
- != userId)) {
- return;
- }
- int registerUid = registerUser.getUid();
- if (allowUids != null && !UserHandle.isSameApp(registerUid, Process.SYSTEM_UID)
- && !ArrayUtils.contains(allowUids, registerUid)) {
- if (DEBUG) {
- Slog.w(TAG, "Skip invoke PackageMonitorCallback for " + intent.getAction()
- + ", uid " + registerUid);
- }
- return;
- }
- Intent newIntent = intent;
- if (filterExtrasFunction != null) {
- final Bundle extras = intent.getExtras();
- if (extras != null) {
- final Bundle filteredExtras = filterExtrasFunction.apply(registerUid, extras);
- if (filteredExtras == null) {
- // caller is unable to access this intent
+ handler.post(() -> {
+ final ArrayList<Pair<IRemoteCallback, Intent>> target = new ArrayList<>();
+ synchronized (mLock) {
+ mCallbacks.broadcast((callback, user) -> {
+ RegisterUser registerUser = (RegisterUser) user;
+ if ((registerUser.getUserId() != UserHandle.USER_ALL)
+ && (registerUser.getUserId() != userId)) {
+ return;
+ }
+ int registerUid = registerUser.getUid();
+ if (allowUids != null && !UserHandle.isSameApp(registerUid, Process.SYSTEM_UID)
+ && !ArrayUtils.contains(allowUids, registerUid)) {
if (DEBUG) {
- Slog.w(TAG,
- "Skip invoke PackageMonitorCallback for " + intent.getAction()
- + " because null filteredExtras");
+ Slog.w(TAG, "Skip invoke PackageMonitorCallback for "
+ + intent.getAction() + ", uid " + registerUid);
}
return;
}
- newIntent = new Intent(newIntent);
- newIntent.replaceExtras(filteredExtras);
- }
+ Intent newIntent = intent;
+ if (filterExtrasFunction != null) {
+ final Bundle extras = intent.getExtras();
+ if (extras != null) {
+ final Bundle filteredExtras =
+ filterExtrasFunction.apply(registerUid, extras);
+ if (filteredExtras == null) {
+ // caller is unable to access this intent
+ if (DEBUG) {
+ Slog.w(TAG,
+ "Skip invoke PackageMonitorCallback for "
+ + intent.getAction()
+ + " because null filteredExtras");
+ }
+ return;
+ }
+ newIntent = new Intent(newIntent);
+ newIntent.replaceExtras(filteredExtras);
+ }
+ }
+ target.add(new Pair<>(callback, newIntent));
+ });
}
- invokeCallback(callback, newIntent);
- }));
+ for (int i = 0; i < target.size(); i++) {
+ Pair<IRemoteCallback, Intent> p = target.get(i);
+ invokeCallback(p.first, p.second);
+ }
+ });
}
private void invokeCallback(IRemoteCallback callback, Intent intent) {
diff --git a/services/core/java/com/android/server/pm/SaferIntentUtils.java b/services/core/java/com/android/server/pm/SaferIntentUtils.java
index 854e142..ec91da9 100644
--- a/services/core/java/com/android/server/pm/SaferIntentUtils.java
+++ b/services/core/java/com/android/server/pm/SaferIntentUtils.java
@@ -213,6 +213,7 @@
* CTS tests. The code in this method shall properly avoid control flows using these arguments.
*/
public static void blockNullAction(IntentArgs args, List componentList) {
+ if (args.intent.getAction() != null) return;
if (ActivityManager.canAccessUnexportedComponents(args.callingUid)) return;
final Computer computer = (Computer) args.snapshot;
@@ -235,14 +236,11 @@
}
final ParsedMainComponent comp = infoToComponent(
resolveInfo.getComponentInfo(), resolver, args.isReceiver);
- if (comp != null && !comp.getIntents().isEmpty()
- && args.intent.getAction() == null) {
+ if (comp != null && !comp.getIntents().isEmpty()) {
match = false;
}
} else if (c instanceof IntentFilter) {
- if (args.intent.getAction() == null) {
- match = false;
- }
+ match = false;
}
if (!match) {
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index 951986f..a09d477 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -227,7 +227,7 @@
}
if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded);
- mBroadcastHelper.sendResourcesChangedBroadcastAndNotify(mPm.snapshotComputer(),
+ mBroadcastHelper.sendResourcesChangedBroadcastAndNotify(mPm::snapshotComputer,
true /* mediaStatus */, false /* replacing */, loaded);
synchronized (mLoadedVolumes) {
mLoadedVolumes.add(vol.getId());
@@ -279,7 +279,7 @@
}
if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded);
- mBroadcastHelper.sendResourcesChangedBroadcastAndNotify(mPm.snapshotComputer(),
+ mBroadcastHelper.sendResourcesChangedBroadcastAndNotify(mPm::snapshotComputer,
false /* mediaStatus */, false /* replacing */, unloaded);
synchronized (mLoadedVolumes) {
mLoadedVolumes.remove(vol.getId());
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index 4e70cc5..88fd1aa 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -192,21 +192,20 @@
}
});
- final Computer newSnapshot = mPm.snapshotComputer();
if (!notifyPackagesList.isEmpty()) {
final String[] changedPackages =
notifyPackagesList.toArray(new String[0]);
- mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
+ mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(mPm::snapshotComputer,
suspended ? Intent.ACTION_PACKAGES_SUSPENDED
: Intent.ACTION_PACKAGES_UNSUSPENDED,
changedPackages, notifyUids.toArray(), quarantined, targetUserId);
- mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, changedPackages,
- suspended, targetUserId);
+ mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(mPm::snapshotComputer,
+ changedPackages, suspended, targetUserId);
mPm.scheduleWritePackageRestrictions(targetUserId);
}
// Send the suspension changed broadcast to ensure suspension state is not stale.
if (!changedPackagesList.isEmpty()) {
- mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
+ mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(mPm::snapshotComputer,
Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
changedPackagesList.toArray(new String[0]), changedUids.toArray(), quarantined,
targetUserId);
@@ -343,13 +342,12 @@
});
mPm.scheduleWritePackageRestrictions(targetUserId);
- final Computer newSnapshot = mPm.snapshotComputer();
if (!unsuspendedPackages.isEmpty()) {
final String[] packageArray = unsuspendedPackages.toArray(
new String[unsuspendedPackages.size()]);
- mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, packageArray,
- false, targetUserId);
- mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
+ mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(mPm::snapshotComputer,
+ packageArray, false, targetUserId);
+ mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(mPm::snapshotComputer,
Intent.ACTION_PACKAGES_UNSUSPENDED,
packageArray, unsuspendedUids.toArray(), false, targetUserId);
}
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 94b49e5..1fda478 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -165,6 +165,22 @@
]
},
{
+ "name": "CtsPackageInstallerCUJUpdateOwnerShipTestCases",
+ "file_patterns": [
+ "core/java/.*Install.*",
+ "services/core/.*Install.*",
+ "services/core/java/com/android/server/pm/.*"
+ ],
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJUpdateSelfTestCases",
"file_patterns": [
"core/java/.*Install.*",
diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java
index 041f2d3..04ce4e6 100644
--- a/services/core/java/com/android/server/pm/UserDataPreparer.java
+++ b/services/core/java/com/android/server/pm/UserDataPreparer.java
@@ -68,6 +68,10 @@
void prepareUserData(UserInfo userInfo, int flags) {
try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ if (storage == null) {
+ Log.e(TAG, "prepareUserData failed due to unable get StorageManager");
+ return;
+ }
/*
* Internal storage must be prepared before adoptable storage, since the user's volume
* keys are stored in their internal storage.
@@ -159,14 +163,16 @@
void destroyUserData(int userId, int flags) {
try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
- /*
- * Volume destruction order isn't really important, but to avoid any weird issues we
- * process internal storage last, the opposite of prepareUserData.
- */
- for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
- final String volumeUuid = vol.getFsUuid();
- if (volumeUuid != null) {
- destroyUserDataLI(volumeUuid, userId, flags);
+ if (storage != null) {
+ /*
+ * Volume destruction order isn't really important, but to avoid any weird issues we
+ * process internal storage last, the opposite of prepareUserData.
+ */
+ for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
+ final String volumeUuid = vol.getFsUuid();
+ if (volumeUuid != null) {
+ destroyUserDataLI(volumeUuid, userId, flags);
+ }
}
}
destroyUserDataLI(null /* internal storage */, userId, flags);
@@ -194,9 +200,10 @@
}
}
- // All the user's data directories should be empty now, so finish the job.
- storage.destroyUserStorage(volumeUuid, userId, flags);
-
+ if (storage != null) {
+ // All the user's data directories should be empty now, so finish the job.
+ storage.destroyUserStorage(volumeUuid, userId, flags);
+ }
} catch (Exception e) {
logCriticalInfo(Log.WARN,
"Failed to destroy user " + userId + " on volume " + volumeUuid + ": " + e);
diff --git a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
index e989d68..e9cb279 100644
--- a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
+++ b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
@@ -40,7 +40,7 @@
import com.android.internal.util.function.DodecFunction;
import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
-import com.android.internal.util.function.NonaFunction;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.TriFunction;
import com.android.internal.util.function.UndecFunction;
@@ -351,22 +351,22 @@
@Override
public SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
@Nullable String featureId, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
- @Nullable String message, boolean shouldCollectMessage, int notedCount,
- @NonNull NonaFunction<Integer, Integer, String, String, Integer, Boolean, String,
- Boolean, Integer, SyncNotedAppOp> superImpl) {
+ @Nullable String message, boolean shouldCollectMessage,
+ @NonNull OctFunction<Integer, Integer, String, String, Integer, Boolean, String,
+ Boolean, SyncNotedAppOp> superImpl) {
if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
try {
return superImpl.apply(code, shellUid, SHELL_PKG, featureId, virtualDeviceId,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage, notedCount);
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
return superImpl.apply(code, uid, packageName, featureId, virtualDeviceId,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage, notedCount);
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage);
}
@Override
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index 33210e2..ecffd38 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -53,7 +53,7 @@
import com.android.internal.util.function.DodecFunction;
import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
-import com.android.internal.util.function.NonaFunction;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.UndecFunction;
import com.android.server.LocalServices;
@@ -246,12 +246,11 @@
public SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
@Nullable String attributionTag, int virtualDeviceId,
boolean shouldCollectAsyncNotedOp, @Nullable String message,
- boolean shouldCollectMessage, int notedCount,
- @NonNull NonaFunction<Integer, Integer, String, String,
- Integer, Boolean, String, Boolean, Integer, SyncNotedAppOp> superImpl) {
+ boolean shouldCollectMessage, @NonNull OctFunction<Integer, Integer, String, String,
+ Integer, Boolean, String, Boolean, SyncNotedAppOp> superImpl) {
return superImpl.apply(resolveDatasourceOp(code, uid, packageName, attributionTag),
resolveUid(code, uid), packageName, attributionTag, virtualDeviceId,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage, notedCount);
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage);
}
@Override
diff --git a/services/core/java/com/android/server/policy/EventLogTags.logtags b/services/core/java/com/android/server/policy/EventLogTags.logtags
index 7563382..a4b6472 100644
--- a/services/core/java/com/android/server/policy/EventLogTags.logtags
+++ b/services/core/java/com/android/server/policy/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package com.android.server.policy
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7c4d425..d5e4c3f 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -88,10 +88,12 @@
import static com.android.hardware.input.Flags.inputManagerLifecycleSupport;
import static com.android.hardware.input.Flags.keyboardA11yShortcutControl;
import static com.android.hardware.input.Flags.modifierShortcutDump;
+import static com.android.hardware.input.Flags.overridePowerKeyBehaviorInFocusedWindow;
import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
+import static com.android.server.GestureLauncherService.DOUBLE_POWER_TAP_COUNT_THRESHOLD;
import static com.android.server.flags.Flags.modifierShortcutManagerMultiuser;
import static com.android.server.flags.Flags.newBugreportKeyboardShortcut;
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
@@ -432,6 +434,16 @@
"android.intent.action.VOICE_ASSIST_RETAIL";
/**
+ * Maximum amount of time in milliseconds between consecutive power onKeyDown events to be
+ * considered a multi-press, only used for the power button.
+ * Note: To maintain backwards compatibility for the power button, we are measuring the times
+ * between consecutive down events instead of the first tap's up event and the second tap's
+ * down event.
+ */
+ @VisibleForTesting public static final int POWER_MULTI_PRESS_TIMEOUT_MILLIS =
+ ViewConfiguration.getMultiPressTimeout();
+
+ /**
* Lock protecting internal state. Must not call out into window
* manager with lock held. (This lock will be acquired in places
* where the window manager is calling in with its own lock held.)
@@ -492,6 +504,32 @@
private WindowWakeUpPolicy mWindowWakeUpPolicy;
+ /**
+ * The three variables below are used for custom power key gesture detection in
+ * PhoneWindowManager. They are used to detect when the power button has been double pressed
+ * and, when it does happen, makes the behavior overrideable by the app.
+ *
+ * We cannot use the {@link PowerKeyRule} for this because multi-press power gesture detection
+ * and behaviors are handled by {@link com.android.server.GestureLauncherService}, and the
+ * {@link PowerKeyRule} only handles single and long-presses of the power button. As a result,
+ * overriding the double tap behavior requires custom gesture detection here that mimics the
+ * logic in {@link com.android.server.GestureLauncherService}.
+ *
+ * Long-term, it would be beneficial to move all power gesture detection to
+ * {@link PowerKeyRule} so that this custom logic isn't required.
+ */
+ // Time of last power down event.
+ private long mLastPowerDown;
+
+ // Number of power button events consecutively triggered (within a specific timeout threshold).
+ private int mPowerButtonConsecutiveTaps = 0;
+
+ // Whether a double tap of the power button has been detected.
+ volatile boolean mDoubleTapPowerDetected;
+
+ // Runnable that is queued on a delay when the first power keyDown event is sent to the app.
+ private Runnable mPowerKeyDelayedRunnable = null;
+
boolean mSafeMode;
// Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key.
@@ -1097,6 +1135,11 @@
mPowerKeyHandled = mPowerKeyHandled || hungUp
|| handledByPowerManager || isKeyGestureTriggered
|| mKeyCombinationManager.isPowerKeyIntercepted();
+
+ if (overridePowerKeyBehaviorInFocusedWindow()) {
+ mPowerKeyHandled |= mDoubleTapPowerDetected;
+ }
+
if (!mPowerKeyHandled) {
if (!interactive) {
wakeUpFromWakeKey(event);
@@ -2669,7 +2712,19 @@
if (mShouldEarlyShortPressOnPower) {
return;
}
- powerPress(downTime, 1 /*count*/, displayId);
+ // TODO(b/380433365): Remove deferring single power press action when refactoring.
+ if (overridePowerKeyBehaviorInFocusedWindow()) {
+ mDeferredKeyActionExecutor.cancelQueuedAction(KEYCODE_POWER);
+ mDeferredKeyActionExecutor.queueKeyAction(
+ KEYCODE_POWER,
+ downTime,
+ () -> {
+ powerPress(downTime, 1 /*count*/, displayId);
+ });
+ } else {
+ powerPress(downTime, 1 /*count*/, displayId);
+ }
+
}
@Override
@@ -2700,7 +2755,17 @@
@Override
void onMultiPress(long downTime, int count, int displayId) {
- powerPress(downTime, count, displayId);
+ if (overridePowerKeyBehaviorInFocusedWindow()) {
+ mDeferredKeyActionExecutor.cancelQueuedAction(KEYCODE_POWER);
+ mDeferredKeyActionExecutor.queueKeyAction(
+ KEYCODE_POWER,
+ downTime,
+ () -> {
+ powerPress(downTime, count, displayId);
+ });
+ } else {
+ powerPress(downTime, count, displayId);
+ }
}
@Override
@@ -3477,6 +3542,12 @@
}
}
+ if (overridePowerKeyBehaviorInFocusedWindow() && event.getKeyCode() == KEYCODE_POWER
+ && event.getAction() == KeyEvent.ACTION_UP
+ && mDoubleTapPowerDetected) {
+ mDoubleTapPowerDetected = false;
+ }
+
return needToConsumeKey ? keyConsumed : keyNotConsumed;
}
@@ -3992,6 +4063,8 @@
sendSystemKeyToStatusBarAsync(event);
return true;
}
+ case KeyEvent.KEYCODE_POWER:
+ return interceptPowerKeyBeforeDispatching(focusedToken, event);
case KeyEvent.KEYCODE_SCREENSHOT:
if (firstDown) {
interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
@@ -4047,6 +4120,8 @@
sendSystemKeyToStatusBarAsync(event);
return true;
}
+ case KeyEvent.KEYCODE_POWER:
+ return interceptPowerKeyBeforeDispatching(focusedToken, event);
}
if (isValidGlobalKey(keyCode)
&& mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) {
@@ -4057,6 +4132,90 @@
return (metaState & KeyEvent.META_META_ON) != 0;
}
+ /**
+ * Called by interceptKeyBeforeDispatching to handle interception logic for KEYCODE_POWER
+ * KeyEvents.
+ *
+ * @return true if intercepting the key, false if sending to app.
+ */
+ private boolean interceptPowerKeyBeforeDispatching(IBinder focusedToken, KeyEvent event) {
+ if (!overridePowerKeyBehaviorInFocusedWindow()) {
+ //Flag disabled: intercept the power key and do not send to app.
+ return true;
+ }
+ if (event.getKeyCode() != KEYCODE_POWER) {
+ Log.wtf(TAG, "interceptPowerKeyBeforeDispatching received a non-power KeyEvent "
+ + "with key code: " + event.getKeyCode());
+ return false;
+ }
+
+ // Intercept keys (don't send to app) for 3x, 4x, 5x gestures)
+ if (mPowerButtonConsecutiveTaps > DOUBLE_POWER_TAP_COUNT_THRESHOLD) {
+ setDeferredKeyActionsExecutableAsync(KEYCODE_POWER, event.getDownTime());
+ return true;
+ }
+
+ // UP key; just reuse the original decision.
+ if (event.getAction() == KeyEvent.ACTION_UP) {
+ final Set<Integer> consumedKeys = mConsumedKeysForDevice.get(event.getDeviceId());
+ return consumedKeys != null
+ && consumedKeys.contains(event.getKeyCode());
+ }
+
+ KeyInterceptionInfo info =
+ mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken);
+
+ if (info == null || !mButtonOverridePermissionChecker.canWindowOverridePowerKey(mContext,
+ info.windowOwnerUid, info.inputFeaturesFlags)) {
+ // The focused window does not have the permission to override power key behavior.
+ if (DEBUG_INPUT) {
+ String interceptReason = "";
+ if (info == null) {
+ interceptReason = "Window is null";
+ } else if (!mButtonOverridePermissionChecker.canAppOverrideSystemKey(mContext,
+ info.windowOwnerUid)) {
+ interceptReason = "Application does not have "
+ + "OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW permission";
+ } else {
+ interceptReason = "Window does not have inputFeatureFlag set";
+ }
+
+ Log.d(TAG, String.format("Intercepting KEYCODE_POWER event. action=%d, "
+ + "eventTime=%d to window=%s. interceptReason=%s. "
+ + "mDoubleTapPowerDetected=%b",
+ event.getAction(), event.getEventTime(), (info != null)
+ ? info.windowTitle : "null", interceptReason,
+ mDoubleTapPowerDetected));
+ }
+ // Intercept the key (i.e. do not send to app)
+ setDeferredKeyActionsExecutableAsync(KEYCODE_POWER, event.getDownTime());
+ return true;
+ }
+
+ if (DEBUG_INPUT) {
+ Log.d(TAG, String.format("Sending KEYCODE_POWER to app. action=%d, "
+ + "eventTime=%d to window=%s. mDoubleTapPowerDetected=%b",
+ event.getAction(), event.getEventTime(), info.windowTitle,
+ mDoubleTapPowerDetected));
+ }
+
+ if (!mDoubleTapPowerDetected) {
+ //Single press: post a delayed runnable for the single press power action that will be
+ // called if it's not cancelled by a double press.
+ final var downTime = event.getDownTime();
+ mPowerKeyDelayedRunnable = () ->
+ setDeferredKeyActionsExecutableAsync(KEYCODE_POWER, downTime);
+ mHandler.postDelayed(mPowerKeyDelayedRunnable, POWER_MULTI_PRESS_TIMEOUT_MILLIS);
+ } else if (mPowerKeyDelayedRunnable != null) {
+ //Double press detected: cancel the single press runnable.
+ mHandler.removeCallbacks(mPowerKeyDelayedRunnable);
+ mPowerKeyDelayedRunnable = null;
+ }
+
+ // Focused window has permission. Send to app.
+ return false;
+ }
+
@SuppressLint("MissingPermission")
private void initKeyGestures() {
if (!useKeyGestureEventHandler()) {
@@ -4081,6 +4240,7 @@
case KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS:
case KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH:
case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT:
case KeyGestureEvent.KEY_GESTURE_TYPE_HOME:
case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS:
case KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN:
@@ -4164,6 +4324,7 @@
}
return true;
case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT:
if (complete) {
launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD,
deviceId, SystemClock.uptimeMillis(),
@@ -4631,6 +4792,11 @@
return true;
}
+ if (overridePowerKeyBehaviorInFocusedWindow() && keyCode == KEYCODE_POWER) {
+ handleUnhandledSystemKey(event);
+ return true;
+ }
+
if (useKeyGestureEventHandler()) {
return false;
}
@@ -5465,8 +5631,13 @@
KeyEvent.actionToString(event.getAction()),
mPowerKeyHandled ? 1 : 0,
mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER));
- // Any activity on the power button stops the accessibility shortcut
- result &= ~ACTION_PASS_TO_USER;
+ if (overridePowerKeyBehaviorInFocusedWindow()) {
+ result |= ACTION_PASS_TO_USER;
+ } else {
+ // Any activity on the power button stops the accessibility shortcut
+ result &= ~ACTION_PASS_TO_USER;
+ }
+
isWakeKey = false; // wake-up will be handled separately
if (down) {
interceptPowerKeyDown(event, interactiveAndAwake, isKeyGestureTriggered);
@@ -5728,6 +5899,35 @@
}
if (event.getKeyCode() == KEYCODE_POWER && event.getAction() == KeyEvent.ACTION_DOWN) {
+ if (overridePowerKeyBehaviorInFocusedWindow()) {
+ if (event.getRepeatCount() > 0) {
+ return;
+ }
+ if (mGestureLauncherService != null) {
+ mGestureLauncherService.processPowerKeyDown(event);
+ }
+
+ if (detectDoubleTapPower(event)) {
+ mDoubleTapPowerDetected = true;
+
+ // Copy of the event for handler in case the original event gets recycled.
+ KeyEvent eventCopy = KeyEvent.obtain(event);
+ mDeferredKeyActionExecutor.queueKeyAction(
+ KeyEvent.KEYCODE_POWER,
+ eventCopy.getEventTime(),
+ () -> {
+ if (!handleCameraGesture(eventCopy, interactive)) {
+ mSingleKeyGestureDetector.interceptKey(
+ eventCopy, interactive, defaultDisplayOn);
+ } else {
+ mSingleKeyGestureDetector.reset();
+ }
+ eventCopy.recycle();
+ });
+ return;
+ }
+ }
+
mPowerKeyHandled = handleCameraGesture(event, interactive);
if (mPowerKeyHandled) {
// handled by camera gesture.
@@ -5739,6 +5939,25 @@
mSingleKeyGestureDetector.interceptKey(event, interactive, defaultDisplayOn);
}
+ private boolean detectDoubleTapPower(KeyEvent event) {
+ if (event.getKeyCode() != KEYCODE_POWER || event.getAction() != KeyEvent.ACTION_DOWN
+ || event.getRepeatCount() != 0) {
+ return false;
+ }
+
+ final long powerTapInterval = event.getEventTime() - mLastPowerDown;
+ mLastPowerDown = event.getEventTime();
+ if (powerTapInterval >= POWER_MULTI_PRESS_TIMEOUT_MILLIS) {
+ // Tap too slow for double press
+ mPowerButtonConsecutiveTaps = 1;
+ } else {
+ mPowerButtonConsecutiveTaps++;
+ }
+
+ return powerTapInterval < POWER_MULTI_PRESS_TIMEOUT_MILLIS
+ && mPowerButtonConsecutiveTaps == DOUBLE_POWER_TAP_COUNT_THRESHOLD;
+ }
+
// The camera gesture will be detected by GestureLauncherService.
private boolean handleCameraGesture(KeyEvent event, boolean interactive) {
// camera gesture.
@@ -7595,6 +7814,12 @@
null)
== PERMISSION_GRANTED;
}
+
+ boolean canWindowOverridePowerKey(Context context, int uid, int inputFeaturesFlags) {
+ return canAppOverrideSystemKey(context, uid)
+ && (inputFeaturesFlags & WindowManager.LayoutParams
+ .INPUT_FEATURE_RECEIVE_POWER_KEY_DOUBLE_PRESS) != 0;
+ }
}
private int getTargetDisplayIdForKeyEvent(KeyEvent event) {
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index f1f1e60..987a849 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -325,36 +325,6 @@
}
SupportInfo supportInfo = new SupportInfo();
- supportInfo.usesSessions = isHintSessionSupported();
- // Global boosts & modes aren't currently relevant for HMS clients
- supportInfo.boosts = 0;
- supportInfo.modes = 0;
- supportInfo.sessionHints = 0;
- supportInfo.sessionModes = 0;
- supportInfo.sessionTags = 0;
- if (isHintSessionSupported()) {
- if (mPowerHalVersion == 4) {
- // Assume we support the V4 hints & modes unless specified
- // otherwise; this is to avoid breaking backwards compat
- // since we historically just assumed they were.
- supportInfo.sessionHints = 31; // first 5 bits are ones
- }
- if (mPowerHalVersion == 5) {
- // Assume we support the V5 hints & modes unless specified
- // otherwise; this is to avoid breaking backwards compat
- // since we historically just assumed they were.
-
- // Hal V5 has 8 modes, all of which it assumes are supported,
- // so we represent that by having the first 8 bits set
- supportInfo.sessionHints = 255; // first 8 bits are ones
- // Hal V5 has 1 mode which it assumes is supported, so we
- // represent that by having the first bit set
- supportInfo.sessionModes = 1;
- // Hal V5 has 5 tags, all of which it assumes are supported,
- // so we represent that by having the first 5 bits set
- supportInfo.sessionTags = 31;
- }
- }
supportInfo.headroom = new SupportInfo.HeadroomSupportInfo();
supportInfo.headroom.isCpuSupported = false;
supportInfo.headroom.isGpuSupported = false;
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index e753ce8..1bed48a 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -163,6 +163,7 @@
private final RollbackPackageHealthObserver mPackageHealthObserver;
private final AppDataRollbackHelper mAppDataRollbackHelper;
private final Runnable mRunExpiration = this::runExpiration;
+ private final PackageWatchdog mPackageWatchdog;
// The # of milli-seconds to sleep for each received ACTION_PACKAGE_ENABLE_ROLLBACK.
// Used by #blockRollbackManager to test timeout in enabling rollbacks.
@@ -190,6 +191,7 @@
mPackageHealthObserver = new RollbackPackageHealthObserver(mContext);
mAppDataRollbackHelper = new AppDataRollbackHelper(mInstaller);
+ mPackageWatchdog = PackageWatchdog.getInstance(mContext);
// Kick off and start monitoring the handler thread.
HandlerThread handlerThread = new HandlerThread("RollbackManagerServiceHandler");
@@ -1249,12 +1251,12 @@
// should document in PackageInstaller.SessionParams#setEnableRollback
// After enabling and committing any rollback, observe packages and
// prepare to rollback if packages crashes too frequently.
- mPackageHealthObserver.startObservingHealth(rollback.getPackageNames(),
- mRollbackLifetimeDurationInMillis);
+ mPackageWatchdog.startExplicitHealthCheck(mPackageHealthObserver,
+ rollback.getPackageNames(), mRollbackLifetimeDurationInMillis);
}
} else {
- mPackageHealthObserver.startObservingHealth(rollback.getPackageNames(),
- mRollbackLifetimeDurationInMillis);
+ mPackageWatchdog.startExplicitHealthCheck(mPackageHealthObserver,
+ rollback.getPackageNames(), mRollbackLifetimeDurationInMillis);
}
runExpiration();
}
@@ -1317,7 +1319,7 @@
}
});
- PackageWatchdog.getInstance(mContext).dump(ipw);
+ mPackageWatchdog.dump(ipw);
}
@AnyThread
diff --git a/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java b/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java
index b9c8d3d..f51c25d 100644
--- a/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java
+++ b/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java
@@ -24,9 +24,14 @@
import android.content.Context;
import android.os.UserManager;
import android.security.advancedprotection.AdvancedProtectionFeature;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Slog;
+import java.util.ArrayList;
+import java.util.List;
+
/** @hide */
public final class DisallowCellular2GAdvancedProtectionHook extends AdvancedProtectionHook {
private static final String TAG = "AdvancedProtectionDisallowCellular2G";
@@ -35,11 +40,13 @@
new AdvancedProtectionFeature(FEATURE_ID_DISALLOW_CELLULAR_2G);
private final DevicePolicyManager mDevicePolicyManager;
private final TelephonyManager mTelephonyManager;
+ private final SubscriptionManager mSubscriptionManager;
public DisallowCellular2GAdvancedProtectionHook(@NonNull Context context, boolean enabled) {
super(context, enabled);
mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
mTelephonyManager = context.getSystemService(TelephonyManager.class);
+ mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
setPolicy(enabled);
}
@@ -50,14 +57,44 @@
return mFeature;
}
+ private static boolean isEmbeddedSubscriptionVisible(SubscriptionInfo subInfo) {
+ if (subInfo.isEmbedded()
+ && (subInfo.getProfileClass() == SubscriptionManager.PROFILE_CLASS_PROVISIONING
+ || (com.android.internal.telephony.flags.Flags.oemEnabledSatelliteFlag()
+ && subInfo.isOnlyNonTerrestrialNetwork()))) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private List<TelephonyManager> getActiveTelephonyManagers() {
+ List<TelephonyManager> telephonyManagers = new ArrayList<>();
+
+ for (SubscriptionInfo subInfo : mSubscriptionManager.getActiveSubscriptionInfoList()) {
+ if (isEmbeddedSubscriptionVisible(subInfo)) {
+ telephonyManagers.add(
+ mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId()));
+ }
+ }
+
+ return telephonyManagers;
+ }
+
@Override
public boolean isAvailable() {
- return mTelephonyManager.isDataCapable();
+ for (TelephonyManager telephonyManager : getActiveTelephonyManagers()) {
+ if (telephonyManager.isDataCapable()
+ && telephonyManager.isRadioInterfaceCapabilitySupported(
+ mTelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK)) {
+ return true;
+ }
+ }
+
+ return false;
}
private void setPolicy(boolean enabled) {
- Slog.i(TAG, "setPolicy called with " + enabled);
-
if (enabled) {
Slog.d(TAG, "Setting DISALLOW_CELLULAR_2G_GLOBALLY restriction");
mDevicePolicyManager.addUserRestrictionGlobally(
@@ -75,12 +112,14 @@
// Leave 2G disabled even if APM is disabled.
if (!enabled) {
- long oldAllowedTypes =
- mTelephonyManager.getAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G);
- long newAllowedTypes = oldAllowedTypes & ~TelephonyManager.NETWORK_CLASS_BITMASK_2G;
- mTelephonyManager.setAllowedNetworkTypesForReason(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, newAllowedTypes);
+ for (TelephonyManager telephonyManager : getActiveTelephonyManagers()) {
+ long oldAllowedTypes =
+ telephonyManager.getAllowedNetworkTypesForReason(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G);
+ long newAllowedTypes = oldAllowedTypes & ~TelephonyManager.NETWORK_CLASS_BITMASK_2G;
+ telephonyManager.setAllowedNetworkTypesForReason(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, newAllowedTypes);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java b/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java
index 0ea88e8..687442b 100644
--- a/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java
+++ b/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java
@@ -28,6 +28,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
public class DataAggregator {
private static final String TAG = "IntrusionDetection DataAggregator";
@@ -36,11 +37,10 @@
private static final int MSG_DISABLE = 2;
private static final int STORED_EVENTS_SIZE_LIMIT = 1024;
- private static final IntrusionDetectionAdminReceiver ADMIN_RECEIVER =
- new IntrusionDetectionAdminReceiver();
private final IntrusionDetectionService mIntrusionDetectionService;
private final ArrayList<DataSource> mDataSources;
+ private final AtomicBoolean mIsLoggingInitialized = new AtomicBoolean(false);
private Context mContext;
private List<IntrusionDetectionEvent> mStoredEvents = new ArrayList<>();
@@ -59,30 +59,20 @@
mHandler = new EventHandler(looper, this);
}
- /**
- * Initialize DataSources
- * @return Whether the initialization succeeds.
- */
- public boolean initialize() {
- SecurityLogSource securityLogSource = new SecurityLogSource(mContext, this);
- mDataSources.add(securityLogSource);
-
- NetworkLogSource networkLogSource = new NetworkLogSource(mContext, this);
- ADMIN_RECEIVER.setNetworkLogEventCallback(networkLogSource);
- mDataSources.add(networkLogSource);
-
- for (DataSource ds : mDataSources) {
- if (!ds.initialize()) {
- return false;
- }
- }
- return true;
+ /** Initialize DataSources */
+ private void initialize() {
+ mDataSources.add(new SecurityLogSource(mContext, this));
+ mDataSources.add(new NetworkLogSource(mContext, this));
}
/**
* Enable the data collection of all DataSources.
*/
public void enable() {
+ if (!mIsLoggingInitialized.get()) {
+ initialize();
+ mIsLoggingInitialized.set(true);
+ }
mHandlerThread = new ServiceThread(TAG, android.os.Process.THREAD_PRIORITY_BACKGROUND,
/* allowIo */ false);
mHandlerThread.start();
@@ -111,9 +101,6 @@
*/
public void disable() {
mHandler.obtainMessage(MSG_DISABLE).sendToTarget();
- for (DataSource ds : mDataSources) {
- ds.disable();
- }
}
private void onNewSingleData(IntrusionDetectionEvent event) {
diff --git a/services/core/java/com/android/server/security/intrusiondetection/DataSource.java b/services/core/java/com/android/server/security/intrusiondetection/DataSource.java
index 61fac46..0bc4482 100644
--- a/services/core/java/com/android/server/security/intrusiondetection/DataSource.java
+++ b/services/core/java/com/android/server/security/intrusiondetection/DataSource.java
@@ -18,11 +18,6 @@
public interface DataSource {
/**
- * Initialize the data source.
- */
- boolean initialize();
-
- /**
* Enable the data collection.
*/
void enable();
diff --git a/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionAdminReceiver.java b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionAdminReceiver.java
deleted file mode 100644
index dba7374..0000000
--- a/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionAdminReceiver.java
+++ /dev/null
@@ -1,42 +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.security.intrusiondetection;
-
-import android.app.admin.DeviceAdminReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Slog;
-
-public class IntrusionDetectionAdminReceiver extends DeviceAdminReceiver {
- private static final String TAG = "IntrusionDetectionAdminReceiver";
-
- private static NetworkLogSource sNetworkLogSource;
-
- @Override
- public void onNetworkLogsAvailable(
- Context context, Intent intent, long batchToken, int networkLogsCount) {
- if (sNetworkLogSource != null) {
- sNetworkLogSource.onNetworkLogsAvailable(batchToken);
- } else {
- Slog.w(TAG, "Network log receiver is not initialized");
- }
- }
-
- public void setNetworkLogEventCallback(NetworkLogSource networkLogSource) {
- sNetworkLogSource = networkLogSource;
- }
-}
diff --git a/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionEventTransportConnection.java b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionEventTransportConnection.java
index b25656e..a16e66d 100644
--- a/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionEventTransportConnection.java
+++ b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionEventTransportConnection.java
@@ -24,42 +24,56 @@
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
+import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.security.intrusiondetection.IIntrusionDetectionEventTransport;
import android.security.intrusiondetection.IntrusionDetectionEvent;
+import android.security.intrusiondetection.IIntrusionDetectionEventTransport;
import android.text.TextUtils;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.infra.AndroidFuture;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.Process;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.concurrent.TimeUnit;
public class IntrusionDetectionEventTransportConnection implements ServiceConnection {
+ private static final String PRODUCTION_BUILD = "user";
+ private static final String PROPERTY_BUILD_TYPE = "ro.build.type";
+ private static final String PROPERTY_INTRUSION_DETECTION_SERVICE_NAME =
+ "debug.intrusiondetection_package_name";
+ private static final long FUTURE_TIMEOUT_MILLIS = 60 * 1000; // 1 min
private static final String TAG = "IntrusionDetectionEventTransportConnection";
- private static final long FUTURE_TIMEOUT_MILLIS = 60 * 1000; // 1 mins
private final Context mContext;
private String mIntrusionDetectionEventTransportConfig;
volatile IIntrusionDetectionEventTransport mService;
+
public IntrusionDetectionEventTransportConnection(Context context) {
mContext = context;
- mService = null;
}
/**
* Initialize the IntrusionDetectionEventTransport binder service.
- * @return Whether the initialization succeed.
+ *
+ * @return Whether the initialization succeeds.
*/
public boolean initialize() {
+ Slog.d(TAG, "initialize");
if (!bindService()) {
return false;
}
+ // Wait for the service to be connected before calling initialize.
+ waitForConnection();
AndroidFuture<Boolean> resultFuture = new AndroidFuture<>();
try {
mService.initialize(resultFuture);
@@ -77,6 +91,20 @@
}
}
+ private void waitForConnection() {
+ synchronized (this) {
+ while (mService == null) {
+ Slog.d(TAG, "waiting for connection to service...");
+ try {
+ this.wait();
+ } catch (InterruptedException e) {
+ /* never interrupted */
+ }
+ }
+ Slog.d(TAG, "connected to service");
+ }
+ }
+
/**
* Add data to the IntrusionDetectionEventTransport binder service.
* @param data List of IntrusionDetectionEvent.
@@ -118,11 +146,42 @@
}
}
+ private String getSystemPropertyValue(String propertyName) {
+ String commandString = "getprop " + propertyName;
+ try {
+ Process process = Runtime.getRuntime().exec(commandString);
+ BufferedReader reader =
+ new BufferedReader(new InputStreamReader(process.getInputStream()));
+ String propertyValue = reader.readLine();
+ reader.close();
+ return propertyValue;
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to get system property value:", e);
+ return null;
+ }
+ }
+
private boolean bindService() {
- mIntrusionDetectionEventTransportConfig = mContext.getString(
- com.android.internal.R.string.config_intrusionDetectionEventTransport);
+ String buildType = getSystemPropertyValue(PROPERTY_BUILD_TYPE);
+ mIntrusionDetectionEventTransportConfig =
+ mContext.getString(
+ com.android.internal.R.string.config_intrusionDetectionEventTransport);
+
+ // If the build type is not production, and a property value is set, use it instead.
+ // This allows us to test the service with a different config.
+ if (!buildType.equals(PRODUCTION_BUILD)
+ && !TextUtils.isEmpty(
+ getSystemPropertyValue(PROPERTY_INTRUSION_DETECTION_SERVICE_NAME))) {
+ mIntrusionDetectionEventTransportConfig =
+ getSystemPropertyValue(PROPERTY_INTRUSION_DETECTION_SERVICE_NAME);
+ }
+ Slog.d(
+ TAG,
+ "mIntrusionDetectionEventTransportConfig: "
+ + mIntrusionDetectionEventTransportConfig);
+
if (TextUtils.isEmpty(mIntrusionDetectionEventTransportConfig)) {
- Slog.e(TAG, "config_intrusionDetectionEventTransport is empty");
+ Slog.e(TAG, "Unable to find a valid config for the transport service");
return false;
}
@@ -163,11 +222,19 @@
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- mService = IIntrusionDetectionEventTransport.Stub.asInterface(service);
+ synchronized (this) {
+ mService = IIntrusionDetectionEventTransport.Stub.asInterface(service);
+ Slog.d(TAG, "connected to service");
+ this.notifyAll();
+ }
}
@Override
public void onServiceDisconnected(ComponentName name) {
- mService = null;
+ synchronized (this) {
+ mService = null;
+ Slog.d(TAG, "disconnected from service");
+ this.notifyAll();
+ }
}
}
diff --git a/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionService.java b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionService.java
index 0287b41..8ff1c7f 100644
--- a/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionService.java
+++ b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionService.java
@@ -232,12 +232,10 @@
return;
}
- // TODO: temporarily disable the following for the CTS IntrusionDetectionManagerTest.
- // Enable it when the transport component is ready.
- // if (!mIntrusionDetectionEventTransportConnection.initialize()) {
- // callback.onFailure(ERROR_TRANSPORT_UNAVAILABLE);
- // return;
- // }
+ if (!mIntrusionDetectionEventTransportConnection.initialize()) {
+ callback.onFailure(ERROR_TRANSPORT_UNAVAILABLE);
+ return;
+ }
mDataAggregator.enable();
mState = STATE_ENABLED;
@@ -252,9 +250,7 @@
return;
}
- // TODO: temporarily disable the following for the CTS IntrusionDetectionManagerTest.
- // Enable it when the transport component is ready.
- // mIntrusionDetectionEventTransportConnection.release();
+ mIntrusionDetectionEventTransportConnection.release();
mDataAggregator.disable();
mState = STATE_DISABLED;
notifyStateMonitors();
diff --git a/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java b/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java
index 1c93d3f..083b1fd 100644
--- a/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java
+++ b/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java
@@ -17,118 +17,131 @@
package com.android.server.security.intrusiondetection;
import android.app.admin.ConnectEvent;
-import android.app.admin.DevicePolicyManager;
import android.app.admin.DnsEvent;
-import android.app.admin.NetworkEvent;
-import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManagerInternal;
+import android.net.IIpConnectivityMetrics;
+import android.net.INetdEventCallback;
+import android.net.metrics.IpConnectivityLog;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.security.intrusiondetection.IntrusionDetectionEvent;
import android.util.Slog;
-import java.util.List;
-import java.util.stream.Collectors;
+import com.android.server.LocalServices;
+import com.android.server.net.BaseNetdEventCallback;
+
+import java.util.concurrent.atomic.AtomicBoolean;
public class NetworkLogSource implements DataSource {
private static final String TAG = "IntrusionDetectionEvent NetworkLogSource";
+ private final AtomicBoolean mIsNetworkLoggingEnabled = new AtomicBoolean(false);
+ private final PackageManagerInternal mPm;
- private DevicePolicyManager mDpm;
- private ComponentName mAdmin;
private DataAggregator mDataAggregator;
- public NetworkLogSource(Context context, DataAggregator dataAggregator) {
+ private IIpConnectivityMetrics mIpConnectivityMetrics;
+ private long mId;
+
+ public NetworkLogSource(Context context, DataAggregator dataAggregator)
+ throws SecurityException {
mDataAggregator = dataAggregator;
- mDpm = context.getSystemService(DevicePolicyManager.class);
- mAdmin = new ComponentName(context, IntrusionDetectionAdminReceiver.class);
+ mPm = LocalServices.getService(PackageManagerInternal.class);
+ mId = 0;
+ initIpConnectivityMetrics();
}
- @Override
- public boolean initialize() {
- try {
- if (!mDpm.isAdminActive(mAdmin)) {
- Slog.e(TAG, "Admin " + mAdmin.flattenToString() + "is not active admin");
- return false;
- }
- } catch (SecurityException e) {
- Slog.e(TAG, "Security exception in initialize: ", e);
- return false;
- }
- return true;
+ private void initIpConnectivityMetrics() {
+ mIpConnectivityMetrics =
+ (IIpConnectivityMetrics)
+ IIpConnectivityMetrics.Stub.asInterface(
+ ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
}
@Override
public void enable() {
- enableNetworkLog();
+ if (mIsNetworkLoggingEnabled.get()) {
+ Slog.w(TAG, "Network logging is already enabled");
+ return;
+ }
+ try {
+ if (mIpConnectivityMetrics.addNetdEventCallback(
+ INetdEventCallback.CALLBACK_CALLER_DEVICE_POLICY, mNetdEventCallback)) {
+ mIsNetworkLoggingEnabled.set(true);
+ } else {
+ Slog.e(TAG, "Failed to enable network logging; invalid callback");
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to enable network logging; ", e);
+ }
}
@Override
public void disable() {
- disableNetworkLog();
- }
-
- private void enableNetworkLog() {
- if (!isNetworkLogEnabled()) {
- mDpm.setNetworkLoggingEnabled(mAdmin, true);
+ if (!mIsNetworkLoggingEnabled.get()) {
+ Slog.w(TAG, "Network logging is already disabled");
+ return;
}
- }
-
- private void disableNetworkLog() {
- if (isNetworkLogEnabled()) {
- mDpm.setNetworkLoggingEnabled(mAdmin, false);
- }
- }
-
- private boolean isNetworkLogEnabled() {
- return mDpm.isNetworkLoggingEnabled(mAdmin);
- }
-
- /**
- * Retrieve network logs when onNetworkLogsAvailable callback is received.
- *
- * @param batchToken The token representing the current batch of network logs.
- */
- public void onNetworkLogsAvailable(long batchToken) {
- List<NetworkEvent> events;
try {
- events = mDpm.retrieveNetworkLogs(mAdmin, batchToken);
- } catch (SecurityException e) {
- Slog.e(
- TAG,
- "Admin "
- + mAdmin.flattenToString()
- + "does not have permission to retrieve network logs",
- e);
- return;
- }
- if (events == null) {
- if (!isNetworkLogEnabled()) {
- Slog.w(TAG, "Network logging is disabled");
+ if (!mIpConnectivityMetrics.removeNetdEventCallback(
+ INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST)) {
+
+ mIsNetworkLoggingEnabled.set(false);
} else {
- Slog.e(TAG, "Invalid batch token: " + batchToken);
+ Slog.e(TAG, "Failed to enable network logging; invalid callback");
}
- return;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to disable network logging; ", e);
}
-
- List<IntrusionDetectionEvent> intrusionDetectionEvents =
- events.stream()
- .filter(event -> event != null)
- .map(event -> toIntrusionDetectionEvent(event))
- .collect(Collectors.toList());
- mDataAggregator.addBatchData(intrusionDetectionEvents);
}
- private IntrusionDetectionEvent toIntrusionDetectionEvent(NetworkEvent event) {
- if (event instanceof DnsEvent) {
- DnsEvent dnsEvent = (DnsEvent) event;
- return new IntrusionDetectionEvent(dnsEvent);
- } else if (event instanceof ConnectEvent) {
- ConnectEvent connectEvent = (ConnectEvent) event;
- return new IntrusionDetectionEvent(connectEvent);
+ private void incrementEventID() {
+ if (mId == Long.MAX_VALUE) {
+ Slog.i(TAG, "Reached maximum id value; wrapping around.");
+ mId = 0;
+ } else {
+ mId++;
}
- throw new IllegalArgumentException(
- "Invalid event type with ID: "
- + event.getId()
- + "from package: "
- + event.getPackageName());
}
+
+ private final INetdEventCallback mNetdEventCallback =
+ new BaseNetdEventCallback() {
+ @Override
+ public void onDnsEvent(
+ int netId,
+ int eventType,
+ int returnCode,
+ String hostname,
+ String[] ipAddresses,
+ int ipAddressesCount,
+ long timestamp,
+ int uid) {
+ if (!mIsNetworkLoggingEnabled.get()) {
+ return;
+ }
+ DnsEvent dnsEvent =
+ new DnsEvent(
+ hostname,
+ ipAddresses,
+ ipAddressesCount,
+ mPm.getNameForUid(uid),
+ timestamp);
+ dnsEvent.setId(mId);
+ incrementEventID();
+ mDataAggregator.addSingleData(new IntrusionDetectionEvent(dnsEvent));
+ }
+
+ @Override
+ public void onConnectEvent(String ipAddr, int port, long timestamp, int uid) {
+ if (!mIsNetworkLoggingEnabled.get()) {
+ return;
+ }
+ ConnectEvent connectEvent =
+ new ConnectEvent(ipAddr, port, mPm.getNameForUid(uid), timestamp);
+ connectEvent.setId(mId);
+ incrementEventID();
+ mDataAggregator.addSingleData(new IntrusionDetectionEvent(connectEvent));
+ }
+ };
}
diff --git a/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java b/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java
index c5f736e..5611905 100644
--- a/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java
+++ b/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java
@@ -43,26 +43,9 @@
mDataAggregator = dataAggregator;
mDpm = context.getSystemService(DevicePolicyManager.class);
mExecutor = Executors.newSingleThreadExecutor();
- }
-
- @Override
- public boolean initialize() {
- // Confirm caller is system and the device is managed. Otherwise logs will
- // be redacted.
- try {
- if (!mDpm.isDeviceManaged()) {
- Slog.e(TAG, "Caller does not have device owner permissions");
- return false;
- }
- } catch (SecurityException e) {
- Slog.e(TAG, "Security exception in initialize: ", e);
- return false;
- }
mEventCallback = new SecurityEventCallback();
- return true;
}
-
@Override
@RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
public void enable() {
@@ -99,6 +82,10 @@
@Override
public void accept(List<SecurityEvent> events) {
+ if (events.size() == 0) {
+ Slog.w(TAG, "No events received; caller may not be authorized");
+ return;
+ }
List<IntrusionDetectionEvent> intrusionDetectionEvents =
events.stream()
.filter(event -> event != null)
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 908f51b..f8877ad 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -129,6 +129,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
@@ -339,7 +341,11 @@
}
@Override
- public void onDisplayAdded(int displayId) {}
+ public void onDisplayAdded(int displayId) {
+ synchronized (mLock) {
+ mDisplayUiState.put(displayId, new UiState());
+ }
+ }
@Override
public void onDisplayRemoved(int displayId) {
@@ -1710,8 +1716,6 @@
icons = new ArrayMap<>(mIcons);
}
synchronized (mLock) {
- // TODO(b/118592525): Currently, status bar only works on the default display.
- // Make it aware of multi-display if needed.
final UiState state = mDisplayUiState.get(DEFAULT_DISPLAY);
return new RegisterStatusBarResult(icons, gatherDisableActionsLocked(mCurrentUserId, 1),
state.mAppearance, state.mAppearanceRegions, state.mImeWindowVis,
@@ -1722,6 +1726,46 @@
}
}
+ @Override
+ public Map<String, RegisterStatusBarResult> registerStatusBarForAllDisplays(IStatusBar bar) {
+ enforceStatusBarService();
+ enforceValidCallingUser();
+
+ Slog.i(TAG, "registerStatusBarForAllDisplays bar=" + bar);
+ mBar = bar;
+ mDeathRecipient.linkToDeath();
+ notifyBarAttachChanged();
+
+ synchronized (mLock) {
+ Map<String, RegisterStatusBarResult> results = new HashMap<>();
+
+ for (int i = 0; i < mDisplayUiState.size(); i++) {
+ final int displayId = mDisplayUiState.keyAt(i);
+ final UiState state = mDisplayUiState.get(displayId);
+
+ final ArrayMap<String, StatusBarIcon> icons;
+ synchronized (mIcons) {
+ icons = new ArrayMap<>(mIcons);
+ }
+
+ if (state != null) {
+ results.put(String.valueOf(displayId),
+ new RegisterStatusBarResult(icons,
+ gatherDisableActionsLocked(mCurrentUserId, 1),
+ state.mAppearance, state.mAppearanceRegions,
+ state.mImeWindowVis,
+ state.mImeBackDisposition, state.mShowImeSwitcher,
+ gatherDisableActionsLocked(mCurrentUserId, 2),
+ state.mNavbarColorManagedByIme, state.mBehavior,
+ state.mRequestedVisibleTypes,
+ state.mPackageName, state.mTransientBarTypes,
+ state.mLetterboxDetails));
+ }
+ }
+ return results;
+ }
+ }
+
private void notifyBarAttachChanged() {
UiThread.getHandler().post(() -> {
if (mGlobalActionListener == null) return;
diff --git a/services/core/java/com/android/server/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
index 63a3e5a..a38fc5b 100644
--- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java
+++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.content.pm.PackageManagerInternal;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -63,7 +64,10 @@
// as this loader (process="system") that's redundant here.
try {
ITelecomLoader telecomLoader = ITelecomLoader.Stub.asInterface(service);
- ITelecomService telecomService = telecomLoader.createTelecomService(mServiceRepo);
+ PackageManagerInternal packageManagerInternal =
+ LocalServices.getService(PackageManagerInternal.class);
+ ITelecomService telecomService = telecomLoader.createTelecomService(mServiceRepo,
+ packageManagerInternal.getSystemUiServiceComponent().getPackageName());
SmsApplication.getDefaultMmsApplication(mContext, false);
ServiceManager.addService(Context.TELECOM_SERVICE, telecomService.asBinder());
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9956d85..3467f94 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -10332,7 +10332,15 @@
.isVisibilityUnknown(this)) {
return false;
}
- if (!isVisibleRequested()) return true;
+ if (!isVisibleRequested()) {
+ // TODO(b/294925498): Remove this finishing check once we have accurate ready tracking.
+ if (task != null && task.getPausingActivity() == this) {
+ // Visibility of starting activities isn't calculated until pause-complete, so if
+ // this is not paused yet, don't consider it ready.
+ return false;
+ }
+ return true;
+ }
if (mPendingRelaunchCount > 0) return false;
// Wait for attach. That is the earliest time where we know if there will be an associated
// display rotation. If we don't wait, the starting-window can finishDrawing first and
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
index 24ed1bb..57a0bb5 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
@@ -18,6 +18,8 @@
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static com.android.server.wm.SnapshotPersistQueue.MAX_STORE_QUEUE_DEPTH;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -343,6 +345,11 @@
if (DEBUG) {
Slog.d(TAG, "ActivitySnapshotController#recordSnapshot " + activity);
}
+ if (mPersister.mSnapshotPersistQueue.peekWriteQueueSize() >= MAX_STORE_QUEUE_DEPTH
+ || mPersister.mSnapshotPersistQueue.peekQueueSize() > MAX_PERSIST_SNAPSHOT_COUNT) {
+ Slog.w(TAG, "Skipping recording activity snapshot, too many requests!");
+ return;
+ }
final int size = activity.size();
final int[] mixedCode = new int[size];
if (size == 1) {
@@ -432,7 +439,7 @@
addBelowActivityIfExist(ar, mPendingLoadActivity, false, "load-snapshot");
} else {
// remove the snapshot for the one below close
- addBelowActivityIfExist(ar, mPendingRemoveActivity, true, "remove-snapshot");
+ addBelowActivityIfExist(ar, mPendingRemoveActivity, false, "remove-snapshot");
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 90d3834..2781592 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -27,6 +27,7 @@
import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.ActivityManager.isStartResultSuccessful;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
import static android.app.PendingIntent.FLAG_ONE_SHOT;
@@ -891,7 +892,10 @@
final ActivityOptions originalOptions = mRequest.activityOptions != null
? mRequest.activityOptions.getOriginalOptions() : null;
// Only track the launch time of activity that will be resumed.
- launchingRecord = mDoResume ? mLastStartActivityRecord : null;
+ if (mDoResume || (isStartResultSuccessful(res)
+ && mLastStartActivityRecord.getTask().isVisibleRequested())) {
+ launchingRecord = mLastStartActivityRecord;
+ }
// If the new record is the one that started, a new activity has created.
final boolean newActivityCreated = mStartActivity == launchingRecord;
// Notify ActivityMetricsLogger that the activity has launched.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 8ff0818..afa7ea1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1555,7 +1555,8 @@
final ActivityRecord[] outActivity = new ActivityRecord[1];
- getActivityStartController().obtainStarter(intent, "dream")
+ final int res = getActivityStartController()
+ .obtainStarter(intent, "dream")
.setCallingUid(callingUid)
.setCallingPid(callingPid)
.setCallingPackage(intent.getPackage())
@@ -1569,9 +1570,11 @@
.execute();
final ActivityRecord started = outActivity[0];
- final IAppTask appTask = started == null ? null :
- new AppTaskImpl(this, started.getTask().mTaskId, callingUid);
- return appTask;
+ if (started == null || !ActivityManager.isStartResultSuccessful(res)) {
+ // start the dream activity failed.
+ return null;
+ }
+ return new AppTaskImpl(this, started.getTask().mTaskId, callingUid);
}
}
@@ -3794,6 +3797,12 @@
r.setPictureInPictureParams(params);
enterPipTransition.setPipActivity(r);
r.mAutoEnteringPip = isAutoEnter;
+
+ if (r.getTaskFragment() != null && r.getTaskFragment().isEmbeddedWithBoundsOverride()
+ && enterPipTransition != null) {
+ enterPipTransition.addFlag(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
+ }
+
getTransitionController().startCollectOrQueue(enterPipTransition, (deferred) -> {
getTransitionController().requestStartTransition(enterPipTransition,
r.getTask(), null /* remoteTransition */, null /* displayChange */);
diff --git a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
index 1676bfa..ae65db4 100644
--- a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
+++ b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
@@ -38,7 +38,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.CameraCompatTaskInfo;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.os.RemoteException;
import android.view.DisplayInfo;
import android.view.Surface;
@@ -180,12 +182,29 @@
if (activity != null) {
activity.recomputeConfiguration();
mCameraTask.dispatchTaskInfoChangedIfNeeded(/* force= */ true);
+ updateCompatibilityInfo(activity);
activity.ensureActivityConfiguration(/* ignoreVisibility= */ true);
} else {
mCameraTask.dispatchTaskInfoChangedIfNeeded(/* force= */ true);
}
}
+ private void updateCompatibilityInfo(@NonNull ActivityRecord activityRecord) {
+ final CompatibilityInfo compatibilityInfo = activityRecord.mAtmService
+ .compatibilityInfoForPackageLocked(activityRecord.info.applicationInfo);
+ compatibilityInfo.applicationDisplayRotation =
+ CameraCompatTaskInfo.getDisplayRotationFromCameraCompatMode(
+ getCameraCompatMode(activityRecord));
+ try {
+ // TODO(b/380840084): Consider using a ClientTransaction for this update.
+ activityRecord.app.getThread().updatePackageCompatibilityInfo(
+ activityRecord.packageName, compatibilityInfo);
+ } catch (RemoteException e) {
+ ProtoLog.w(WmProtoLogGroups.WM_DEBUG_STATES,
+ "Unable to update CompatibilityInfo for app %s", activityRecord.app);
+ }
+ }
+
boolean shouldCameraCompatControlOrientation(@NonNull ActivityRecord activity) {
return isCameraRunningAndWindowingModeEligible(activity);
}
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index 93ccd74..6ccceb9 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -243,6 +243,19 @@
}
}
+ /** Called when the surface of display is changed to a different instance. */
+ void resetRecordingDisplay(int displayId) {
+ if (!isCurrentlyRecording()
+ || mContentRecordingSession.getDisplayToRecord() != displayId) {
+ return;
+ }
+ ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+ "Content Recording: Display %d changed surface so stop recording", displayId);
+ mDisplayContent.mWmService.mTransactionFactory.get().remove(mRecordedSurface).apply();
+ mRecordedSurface = null;
+ // Do not un-set the token, in case new surface is ready and recording should begin again.
+ }
+
/**
* Pauses recording on this display content. Note the session does not need to be updated,
* since recording can be resumed still.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e190963..c2141a7 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -260,6 +260,7 @@
import com.android.server.wm.utils.RegionUtils;
import com.android.server.wm.utils.RotationCache;
import com.android.server.wm.utils.WmDisplayCutout;
+import com.android.window.flags.Flags;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -1272,7 +1273,13 @@
@Override
void migrateToNewSurfaceControl(Transaction t) {
t.remove(mSurfaceControl);
-
+ // Reset the recording displays which were mirroring this display.
+ for (int i = mRootWindowContainer.getChildCount() - 1; i >= 0; i--) {
+ final ContentRecorder recorder = mRootWindowContainer.getChildAt(i).mContentRecorder;
+ if (recorder != null) {
+ recorder.resetRecordingDisplay(mDisplayId);
+ }
+ }
mLastSurfacePosition.set(0, 0);
mLastDeltaRotation = Surface.ROTATION_0;
@@ -1384,11 +1391,16 @@
mTokenMap.put(binder, token);
if (token.asActivityRecord() == null) {
- // Set displayContent for non-app token to prevent same token will add twice after
- // onDisplayChanged.
- // TODO: Check if it's fine that super.onDisplayChanged of WindowToken
- // (WindowsContainer#onDisplayChanged) may skipped when token.mDisplayContent assigned.
- token.mDisplayContent = this;
+ // Setting the mDisplayContent to the token is not needed: it is done by da.addChild
+ // below, that also calls onDisplayChanged once moved.
+ if (!Flags.reparentWindowTokenApi()) {
+ // Set displayContent for non-app token to prevent same token will add twice after
+ // onDisplayChanged.
+ // TODO: Check if it's fine that super.onDisplayChanged of WindowToken
+ // (WindowsContainer#onDisplayChanged) may skipped when token.mDisplayContent
+ // assigned.
+ token.mDisplayContent = this;
+ }
// Add non-app token to container hierarchy on the display. App tokens are added through
// the parent container managing them (e.g. Tasks).
final DisplayArea.Tokens da = findAreaForToken(token).asTokens();
@@ -2274,7 +2286,7 @@
if (shellTransitions) {
// Before setDisplayProjection is applied by the start transaction of transition,
// set the transform hint to avoid using surface in old rotation.
- getPendingTransaction().setFixedTransformHint(mSurfaceControl, rotation);
+ setFixedTransformHint(getPendingTransaction(), mSurfaceControl, rotation);
// The sync transaction should already contains setDisplayProjection, so unset the
// hint to restore the natural state when the transaction is applied.
transaction.unsetFixedTransformHint(mSurfaceControl);
@@ -2284,6 +2296,10 @@
mWmService.mRotationWatcherController.dispatchDisplayRotationChange(mDisplayId, rotation);
}
+ void setFixedTransformHint(Transaction t, SurfaceControl sc, int rotation) {
+ t.setFixedTransformHint(sc, (rotation + mDisplayInfo.installOrientation) % 4);
+ }
+
void configureDisplayPolicy() {
mRootWindowContainer.updateDisplayImePolicyCache();
mDisplayPolicy.updateConfigurationAndScreenSizeDependentBehaviors();
@@ -7109,9 +7125,11 @@
/**
* @see #getRequestedVisibleTypes()
*/
- void setRequestedVisibleTypes(@InsetsType int requestedVisibleTypes) {
- if (mRequestedVisibleTypes != requestedVisibleTypes) {
- mRequestedVisibleTypes = requestedVisibleTypes;
+ void updateRequestedVisibleTypes(@InsetsType int visibleTypes, @InsetsType int mask) {
+ int newRequestedVisibleTypes =
+ (mRequestedVisibleTypes & ~mask) | (visibleTypes & mask);
+ if (mRequestedVisibleTypes != newRequestedVisibleTypes) {
+ mRequestedVisibleTypes = newRequestedVisibleTypes;
}
}
}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 2a5a3a5..1c4e487 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -528,7 +528,7 @@
}
// Only allow the extras to be dispatched to a global-intercepting drag target
ClipData data = null;
- if (interceptsGlobalDrag) {
+ if (interceptsGlobalDrag && mData != null) {
data = mData.copyForTransferWithActivityInfo();
PersistableBundle extras = data.getDescription().getExtras() != null
? data.getDescription().getExtras()
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 27f82d9..7fdc2c6 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -40,7 +40,7 @@
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_TASKS;
-import static com.android.launcher3.Flags.enableRefactorTaskThumbnail;
+import static com.android.launcher3.Flags.enableUseTopVisibleActivityForExcludeFromRecentTask;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
@@ -1529,7 +1529,7 @@
// The Recents is only supported on default display now, we should only keep the
// most recent task of home display.
boolean isMostRecentTask;
- if (enableRefactorTaskThumbnail()) {
+ if (enableUseTopVisibleActivityForExcludeFromRecentTask()) {
isMostRecentTask = task.getTopVisibleActivity() != null;
} else {
isMostRecentTask = taskIndex == 0;
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index c20b858..8f0f686 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -56,7 +56,7 @@
mOldRotation = oldRotation;
mNewRotation = newRotation;
mApplyFixedTransformHint = applyFixedTransformationHint;
- mFixedTransformHint = oldRotation;
+ mFixedTransformHint = (oldRotation + info.installOrientation) % 4;
final boolean flipped = info.rotation == ROTATION_90 || info.rotation == ROTATION_270;
final int pH = flipped ? info.logicalWidth : info.logicalHeight;
final int pW = flipped ? info.logicalHeight : info.logicalWidth;
diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
index 8b63ecf7..a545454 100644
--- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
+++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
@@ -50,7 +50,7 @@
class SnapshotPersistQueue {
private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotPersister" : TAG_WM;
private static final long DELAY_MS = 100;
- private static final int MAX_STORE_QUEUE_DEPTH = 2;
+ static final int MAX_STORE_QUEUE_DEPTH = 2;
private static final int COMPRESS_QUALITY = 95;
@GuardedBy("mLock")
@@ -154,7 +154,12 @@
}
}
- @VisibleForTesting
+ int peekWriteQueueSize() {
+ synchronized (mLock) {
+ return mStoreQueueItems.size();
+ }
+ }
+
int peekQueueSize() {
synchronized (mLock) {
return mWriteQueue.size();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 0696aa0..c8befb2 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3425,6 +3425,7 @@
info.isTopActivityNoDisplay = top != null && top.isNoDisplay();
info.isSleeping = shouldSleepActivities();
info.isTopActivityTransparent = top != null && !top.fillsParent();
+ info.isActivityStackTransparent = !topTask.forAllActivities(r -> (r.occludesParent()));
info.lastNonFullscreenBounds = topTask.mLastNonFullscreenBounds;
final WindowState windowState = top != null
? top.findMainWindow(/* includeStartingApp= */ false) : null;
@@ -4491,7 +4492,7 @@
}
void onPictureInPictureParamsChanged() {
- if (inPinnedWindowingMode()) {
+ if (inPinnedWindowingMode() || Flags.enableDesktopWindowingPip()) {
dispatchTaskInfoChangedIfNeeded(true /* force */);
}
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 20481f2..1fc609b7 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1429,7 +1429,16 @@
// Commit wallpaper visibility after activity, because usually the wallpaper target token is
// an activity, and wallpaper's visibility depends on activity's visibility.
for (int i = mParticipants.size() - 1; i >= 0; --i) {
- final WallpaperWindowToken wt = mParticipants.valueAt(i).asWallpaperToken();
+ final WindowContainer<?> wc = mParticipants.valueAt(i);
+ WallpaperWindowToken wt = wc.asWallpaperToken();
+ if (!Flags.ensureWallpaperInTransitions()) {
+ if (wt == null) {
+ final WindowState windowState = wc.asWindowState();
+ if (windowState != null) {
+ wt = windowState.mToken.asWallpaperToken();
+ }
+ }
+ }
if (wt == null) continue;
final WindowState target = wt.mDisplayContent.mWallpaperController.getWallpaperTarget();
final boolean isTargetInvisible = target == null || !target.mToken.isVisible();
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 8562bb2..f3c03cb 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -791,19 +791,30 @@
ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS,
"Requesting StartTransition: %s", transition);
ActivityManager.RunningTaskInfo startTaskInfo = null;
- ActivityManager.RunningTaskInfo pipTaskInfo = null;
+ TransitionRequestInfo.PipChange pipChange = null;
if (startTask != null) {
startTaskInfo = startTask.getTaskInfo();
}
// set the pip task in the request if provided
if (transition.getPipActivity() != null) {
- pipTaskInfo = transition.getPipActivity().getTask().getTaskInfo();
+ ActivityManager.RunningTaskInfo pipTaskInfo =
+ transition.getPipActivity().getTask().getTaskInfo();
+ ActivityRecord pipActivity = transition.getPipActivity();
+ if (pipActivity.getTaskFragment() != null
+ && pipActivity.getTaskFragment() != pipActivity.getTask()) {
+ // If the PiP activity is in a TF different from its task, this could be
+ // AE-to-PiP case, so PipChange will have the TF token cached separately.
+ pipChange = new TransitionRequestInfo.PipChange(pipActivity.getTaskFragment()
+ .mRemoteToken.toWindowContainerToken(), pipTaskInfo);
+ } else {
+ pipChange = new TransitionRequestInfo.PipChange(pipTaskInfo);
+ }
transition.setPipActivity(null);
}
final TransitionRequestInfo request = new TransitionRequestInfo(transition.mType,
- startTaskInfo, pipTaskInfo, remoteTransition, displayChange,
+ startTaskInfo, pipChange, remoteTransition, displayChange,
transition.getFlags(), transition.getSyncId());
transition.mLogger.mRequestTimeNs = SystemClock.elapsedRealtimeNanos();
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 2397e03..aa60f93 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3733,7 +3733,7 @@
&& !mTransitionController.useShellTransitionsRotation()) {
if (deltaRotation != Surface.ROTATION_0) {
updateSurfaceRotation(t, deltaRotation, null /* positionLeash */);
- getPendingTransaction().setFixedTransformHint(mSurfaceControl,
+ mDisplayContent.setFixedTransformHint(getPendingTransaction(), mSurfaceControl,
getWindowConfiguration().getDisplayRotation());
} else if (deltaRotation != mLastDeltaRotation) {
t.setMatrix(mSurfaceControl, 1, 0, 0, 1);
diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java
index 809745e..6d0da1f 100644
--- a/services/core/java/com/android/server/wm/WindowContextListenerController.java
+++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java
@@ -167,6 +167,22 @@
return true;
}
+ boolean assertCallerCanReparentListener(@NonNull IBinder clientToken,
+ boolean callerCanManageAppTokens, int callingUid, int displayId) {
+ if (!assertCallerCanModifyListener(clientToken, callerCanManageAppTokens, callingUid)) {
+ return false;
+ }
+
+ final WindowContainer<?> container = getContainer(clientToken);
+ if (container != null && container.getDisplayContent() != null
+ && container.getDisplayContent().mDisplayId == displayId) {
+ ProtoLog.i(WM_DEBUG_ADD_REMOVE,
+ "The listener has already been attached to the same display id");
+ return false;
+ }
+ return true;
+ }
+
boolean hasListener(IBinder clientToken) {
return mListeners.containsKey(clientToken);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7e70e75..0b6ca75 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -358,6 +358,7 @@
import com.android.server.power.ShutdownThread;
import com.android.server.utils.PriorityDump;
import com.android.server.wallpaper.WallpaperCropper.WallpaperCropUtils;
+import com.android.window.flags.Flags;
import dalvik.annotation.optimization.NeverCompile;
@@ -3042,6 +3043,81 @@
}
}
+ @Override
+ public boolean reparentWindowContextToDisplayArea(
+ @NonNull IApplicationThread appThread, @NonNull IBinder clientToken, int displayId) {
+ if (!Flags.reparentWindowTokenApi()) {
+ return false;
+ }
+ Objects.requireNonNull(appThread);
+ Objects.requireNonNull(clientToken);
+ final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS,
+ "reparentWindowContextToDisplayArea", false /* printLog */);
+ final int callingPid = Binder.getCallingPid();
+ final int callingUid = Binder.getCallingUid();
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final WindowProcessController wpc = mAtmService.getProcessController(appThread);
+ if (wpc == null) {
+ ProtoLog.w(WM_ERROR, "reparentWindowContextToDisplayArea: calling from"
+ + " non-existing process pid=%d uid=%d", callingPid, callingUid);
+ return false;
+ }
+ final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId);
+ if (dc == null) {
+ ProtoLog.w(WM_ERROR, "reparentWindowContextToDisplayArea: trying to attach"
+ + " to a non-existing display:%d", displayId);
+ return false;
+ }
+
+ if (!mWindowContextListenerController.assertCallerCanReparentListener(clientToken,
+ callerCanManageAppTokens, callingUid, displayId)) {
+ return false;
+ }
+ final WindowContainer<?> container = mWindowContextListenerController.getContainer(
+ clientToken);
+
+ final WindowToken token = container != null ? container.asWindowToken() : null;
+ if (token != null && token.isFromClient()) {
+ ProtoLog.d(WM_DEBUG_ADD_REMOVE, "Reparenting from dc to displayId=%d",
+ displayId);
+ // Reparent the window created for this window context.
+ dc.reParentWindowToken(token);
+ hideUntilNextDraw(token);
+ // This makes sure there is a traversal scheduled that will eventually report
+ // the window resize to the client.
+ dc.setLayoutNeeded();
+ requestTraversal();
+ return true;
+ }
+
+ final int type = mWindowContextListenerController.getWindowType(clientToken);
+ final Bundle options = mWindowContextListenerController.getOptions(clientToken);
+ // No window yet, switch listening DA.
+ final DisplayArea<?> da = dc.findAreaForWindowType(type, options,
+ callerCanManageAppTokens, false /* roundedCornerOverlay */);
+ mWindowContextListenerController.registerWindowContainerListener(wpc, clientToken,
+ da, type, options, true /* shouldDispatchConfigWhenRegistering */);
+ return true;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ private void hideUntilNextDraw(@NonNull WindowToken token) {
+ final WindowState topChild = token.getTopChild();
+ if (topChild != null) {
+ mTransactionFactory.get().hide(token.mSurfaceControl).apply();
+ topChild.applyWithNextDraw(t -> {
+ if (token.mSurfaceControl != null) {
+ t.show(token.mSurfaceControl);
+ }
+ });
+ }
+ }
+
/** Returns {@code true} if this binder is a registered window token. */
@Override
public boolean isWindowToken(IBinder binder) {
@@ -4671,7 +4747,8 @@
@EnforcePermission(android.Manifest.permission.MANAGE_APP_TOKENS)
@Override
public void updateDisplayWindowRequestedVisibleTypes(int displayId,
- @InsetsType int requestedVisibleTypes, @Nullable ImeTracker.Token statsToken) {
+ @InsetsType int visibleTypes, @InsetsType int mask,
+ @Nullable ImeTracker.Token statsToken) {
updateDisplayWindowRequestedVisibleTypes_enforcePermission();
final long origId = Binder.clearCallingIdentity();
try {
@@ -4684,7 +4761,7 @@
}
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_WM_UPDATE_DISPLAY_WINDOW_REQUESTED_VISIBLE_TYPES);
- dc.mRemoteInsetsControlTarget.setRequestedVisibleTypes(requestedVisibleTypes);
+ dc.mRemoteInsetsControlTarget.updateRequestedVisibleTypes(visibleTypes, mask);
// TODO(b/353463205) the statsToken shouldn't be null as it is used later in the
// IME provider. Check if we have to create a new request here, if null.
dc.getInsetsStateController().onRequestedVisibleTypesChanged(
@@ -7949,43 +8026,46 @@
@Override
public void waitForAllWindowsDrawn(Message message, long timeout, int displayId) {
Objects.requireNonNull(message.getTarget());
- final WindowContainer<?> container = displayId == INVALID_DISPLAY
- ? mRoot : mRoot.getDisplayContent(displayId);
- if (container == null) {
- // The waiting container doesn't exist, no need to wait to run the callback. Run and
- // return;
- message.sendToTarget();
- return;
- }
boolean allWindowsDrawn = false;
synchronized (mGlobalLock) {
- if (displayId == INVALID_DISPLAY
- && mRoot.getDefaultDisplay().mDisplayUpdater.waitForTransition(message)) {
- // Use the ready-to-play of transition as the signal.
- return;
- }
- container.waitForAllWindowsDrawn();
- mWindowPlacerLocked.requestTraversal();
- mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container);
- if (container.mWaitingForDrawn.isEmpty()) {
- allWindowsDrawn = true;
- } else {
- if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
- for (int i = 0; i < container.mWaitingForDrawn.size(); i++) {
- traceStartWaitingForWindowDrawn(container.mWaitingForDrawn.get(i));
- }
- }
-
- mWaitingForDrawnCallbacks.put(container, message);
- mH.sendNewMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, container, timeout);
- checkDrawnWindowsLocked();
- }
+ allWindowsDrawn = waitForAllWindowsDrawnLocked(message, timeout, displayId);
}
if (allWindowsDrawn) {
message.sendToTarget();
}
}
+ /** Return {@code true} if all windows have been drawn. */
+ private boolean waitForAllWindowsDrawnLocked(Message message, long timeout, int displayId) {
+ final WindowContainer<?> container = displayId == INVALID_DISPLAY
+ ? mRoot : mRoot.getDisplayContent(displayId);
+ if (container == null) {
+ // The waiting container doesn't exist, no need to wait. Treat as drawn.
+ return true;
+ }
+ if (displayId == INVALID_DISPLAY
+ && mRoot.getDefaultDisplay().mDisplayUpdater.waitForTransition(message)) {
+ // Use the ready-to-play of transition as the signal.
+ return false;
+ }
+ container.waitForAllWindowsDrawn();
+ mWindowPlacerLocked.requestTraversal();
+ mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container);
+ if (container.mWaitingForDrawn.isEmpty()) {
+ return true;
+ }
+ if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
+ for (int i = 0; i < container.mWaitingForDrawn.size(); i++) {
+ traceStartWaitingForWindowDrawn(container.mWaitingForDrawn.get(i));
+ }
+ }
+
+ mWaitingForDrawnCallbacks.put(container, message);
+ mH.sendNewMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, container, timeout);
+ checkDrawnWindowsLocked();
+ return false;
+ }
+
@Override
public void setForcedDisplaySize(int displayId, int width, int height) {
WindowManagerService.this.setForcedDisplaySize(displayId, width, height);
@@ -9016,16 +9096,19 @@
clearPointerDownOutsideFocusRunnable();
+ final InputTarget focusedInputTarget = mFocusedInputTarget;
if (shouldDelayTouchOutside(t)) {
- mPointerDownOutsideFocusRunnable = () -> handlePointerDownOutsideFocus(t);
+ mPointerDownOutsideFocusRunnable =
+ () -> handlePointerDownOutsideFocus(t, focusedInputTarget);
mH.postDelayed(mPointerDownOutsideFocusRunnable, POINTER_DOWN_OUTSIDE_FOCUS_TIMEOUT_MS);
} else if (!fromHandler) {
// Still post the runnable to handler thread in case there is already a runnable
// in execution, but still waiting to hold the wm lock.
- mPointerDownOutsideFocusRunnable = () -> handlePointerDownOutsideFocus(t);
+ mPointerDownOutsideFocusRunnable =
+ () -> handlePointerDownOutsideFocus(t, focusedInputTarget);
mH.post(mPointerDownOutsideFocusRunnable);
} else {
- handlePointerDownOutsideFocus(t);
+ handlePointerDownOutsideFocus(t, focusedInputTarget);
}
}
@@ -9057,8 +9140,15 @@
return shouldDelayTouchForEmbeddedActivity || shouldDelayTouchForFreeform;
}
- private void handlePointerDownOutsideFocus(InputTarget t) {
+ private void handlePointerDownOutsideFocus(InputTarget t, InputTarget focusedInputTarget) {
synchronized (mGlobalLock) {
+ if (mFocusedInputTarget != focusedInputTarget) {
+ // Skip if the mFocusedInputTarget is already changed. This is possible if the
+ // pointer-down-outside-focus event is delayed to be handled.
+ ProtoLog.i(WM_DEBUG_FOCUS_LIGHT,
+ "Skip onPointerDownOutsideFocusLocked due to input target changed %s", t);
+ return;
+ }
if (mPointerDownOutsideFocusRunnable != null
&& mH.hasCallbacks(mPointerDownOutsideFocusRunnable)) {
// Skip if there's another pending pointer-down-outside-focus event.
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index ddff24d..66921ff 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1335,11 +1335,11 @@
}
case HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK: {
final WindowContainer container = WindowContainer.fromBinder(hop.getContainer());
- Task pipTask = container.asTask();
- if (pipTask == null) {
+ TaskFragment pipTaskFragment = container.asTaskFragment();
+ if (pipTaskFragment == null) {
break;
}
- ActivityRecord pipActivity = pipTask.getActivity(
+ ActivityRecord pipActivity = pipTaskFragment.getActivity(
(activity) -> activity.pictureInPictureArgs != null);
if (pipActivity.isState(RESUMED)) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index cebe790..447d443 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5750,9 +5750,10 @@
|| mKeyInterceptionInfo.layoutParamsPrivateFlags != getAttrs().privateFlags
|| mKeyInterceptionInfo.layoutParamsType != getAttrs().type
|| mKeyInterceptionInfo.windowTitle != getWindowTag()
- || mKeyInterceptionInfo.windowOwnerUid != getOwningUid()) {
+ || mKeyInterceptionInfo.windowOwnerUid != getOwningUid()
+ || mKeyInterceptionInfo.inputFeaturesFlags != getAttrs().inputFeatures) {
mKeyInterceptionInfo = new KeyInterceptionInfo(getAttrs().type, getAttrs().privateFlags,
- getWindowTag().toString(), getOwningUid());
+ getWindowTag().toString(), getOwningUid(), getAttrs().inputFeatures);
}
return mKeyInterceptionInfo;
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 004f406..cca73c57 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -49,6 +49,7 @@
import com.android.internal.protolog.ProtoLog;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.window.flags.Flags;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -386,7 +387,15 @@
@Override
void onDisplayChanged(DisplayContent dc) {
- dc.reParentWindowToken(this);
+ if (!Flags.reparentWindowTokenApi()) {
+ dc.reParentWindowToken(this);
+ } else {
+ // This check is needed to break recursion, as DisplayContent#reparentWindowToken also
+ // triggers a WindowToken#onDisplayChanged.
+ if (dc.getWindowToken(token) == null) {
+ dc.reParentWindowToken(this);
+ }
+ }
// TODO(b/36740756): One day this should perhaps be hooked
// up with goodToGo, so we don't move a window
@@ -629,7 +638,7 @@
.build();
t.setPosition(leash, mLastSurfacePosition.x, mLastSurfacePosition.y);
t.reparent(getSurfaceControl(), leash);
- getPendingTransaction().setFixedTransformHint(leash,
+ mDisplayContent.setFixedTransformHint(getPendingTransaction(), leash,
getWindowConfiguration().getDisplayRotation());
mFixedRotationTransformLeash = leash;
updateSurfaceRotation(t, rotation, mFixedRotationTransformLeash);
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 9430194..0464230 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -68,6 +68,7 @@
#include <map>
#include <vector>
+#include "android_hardware_display_DisplayTopology.h"
#include "android_hardware_display_DisplayViewport.h"
#include "android_hardware_input_InputApplicationHandle.h"
#include "android_hardware_input_InputWindowHandle.h"
@@ -321,6 +322,8 @@
void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
+ void setDisplayTopology(JNIEnv* env, jobject topologyGraph);
+
base::Result<std::unique_ptr<InputChannel>> createInputChannel(const std::string& name);
base::Result<std::unique_ptr<InputChannel>> createInputMonitor(ui::LogicalDisplayId displayId,
const std::string& name,
@@ -362,7 +365,7 @@
void setMotionClassifierEnabled(bool enabled);
std::optional<std::string> getBluetoothAddress(int32_t deviceId);
void setStylusButtonMotionEventsEnabled(bool enabled);
- FloatPoint getMouseCursorPosition(ui::LogicalDisplayId displayId);
+ vec2 getMouseCursorPosition(ui::LogicalDisplayId displayId);
void setStylusPointerIconEnabled(bool enabled);
void setInputMethodConnectionIsActive(bool isActive);
void setKeyRemapping(const std::map<int32_t, int32_t>& keyRemapping);
@@ -441,7 +444,7 @@
std::shared_ptr<PointerControllerInterface> createPointerController(
PointerControllerInterface::ControllerType type) override;
void notifyPointerDisplayIdChanged(ui::LogicalDisplayId displayId,
- const FloatPoint& position) override;
+ const vec2& position) override;
void notifyMouseCursorFadedOnTyping() override;
/* --- InputFilterPolicyInterface implementation --- */
@@ -640,6 +643,11 @@
InputReaderConfiguration::Change::DISPLAY_INFO);
}
+void NativeInputManager::setDisplayTopology(JNIEnv* env, jobject topologyGraph) {
+ android_hardware_display_DisplayTopologyGraph_toNative(env, topologyGraph);
+ // TODO(b/367661489): Use the topology
+}
+
base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputChannel(
const std::string& name) {
ATRACE_CALL();
@@ -871,7 +879,7 @@
}
void NativeInputManager::notifyPointerDisplayIdChanged(ui::LogicalDisplayId pointerDisplayId,
- const FloatPoint& position) {
+ const vec2& position) {
// Notify the Reader so that devices can be reconfigured.
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -2023,7 +2031,7 @@
InputReaderConfiguration::Change::STYLUS_BUTTON_REPORTING);
}
-FloatPoint NativeInputManager::getMouseCursorPosition(ui::LogicalDisplayId displayId) {
+vec2 NativeInputManager::getMouseCursorPosition(ui::LogicalDisplayId displayId) {
return mInputManager->getChoreographer().getMouseCursorPosition(displayId);
}
@@ -2092,6 +2100,12 @@
im->setDisplayViewports(env, viewportObjArray);
}
+static void nativeSetDisplayTopology(JNIEnv* env, jobject nativeImplObj,
+ jobject displayTopologyObj) {
+ NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+ im->setDisplayTopology(env, displayTopologyObj);
+}
+
static jint nativeGetScanCodeState(JNIEnv* env, jobject nativeImplObj, jint deviceId,
jint sourceMask, jint scanCode) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
@@ -3148,6 +3162,8 @@
{"start", "()V", (void*)nativeStart},
{"setDisplayViewports", "([Landroid/hardware/display/DisplayViewport;)V",
(void*)nativeSetDisplayViewports},
+ {"setDisplayTopology", "(Landroid/hardware/display/DisplayTopologyGraph;)V",
+ (void*)nativeSetDisplayTopology},
{"getScanCodeState", "(III)I", (void*)nativeGetScanCodeState},
{"getKeyCodeState", "(III)I", (void*)nativeGetKeyCodeState},
{"getSwitchState", "(III)I", (void*)nativeGetSwitchState},
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index df37ec3..09fd8d4 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -49,6 +49,7 @@
int register_android_server_HardwarePropertiesManagerService(JNIEnv* env);
int register_android_server_SyntheticPasswordManager(JNIEnv* env);
int register_android_hardware_display_DisplayViewport(JNIEnv* env);
+int register_android_hardware_display_DisplayTopology(JNIEnv* env);
int register_android_server_am_OomConnection(JNIEnv* env);
int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
int register_android_server_am_Freezer(JNIEnv* env);
@@ -114,6 +115,7 @@
register_android_server_storage_AppFuse(env);
register_android_server_SyntheticPasswordManager(env);
register_android_hardware_display_DisplayViewport(env);
+ register_android_hardware_display_DisplayTopology(env);
register_android_server_am_OomConnection(env);
register_android_server_am_CachedAppOptimizer(env);
register_android_server_am_Freezer(env);
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index e73dacb..0c9a89b 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.credentials.CredentialManager;
import android.credentials.CredentialProviderInfo;
+import android.credentials.flags.Flags;
import android.credentials.selection.DisabledProviderData;
import android.credentials.selection.IntentCreationResult;
import android.credentials.selection.IntentFactory;
@@ -46,6 +47,12 @@
/** Initiates the Credential Manager UI and receives results. */
public class CredentialManagerUi {
+
+ private static final String SESSION_ID_TRACK_ONE =
+ "com.android.server.credentials.CredentialManagerUi.SESSION_ID_TRACK_ONE";
+ private static final String SESSION_ID_TRACK_TWO =
+ "com.android.server.credentials.CredentialManagerUi.SESSION_ID_TRACK_TWO";
+
@NonNull
private final CredentialManagerUiCallback mCallbacks;
@NonNull
@@ -148,8 +155,8 @@
* by the calling app process. The bottom-sheet navigates to the default page when the intent
* is invoked.
*
- * @param requestInfo the information about the request
- * @param providerDataList the list of provider data from remote providers
+ * @param requestInfo the information about the request
+ * @param providerDataList the list of provider data from remote providers
*/
public PendingIntent createPendingIntent(
RequestInfo requestInfo, ArrayList<ProviderData> providerDataList,
@@ -175,6 +182,11 @@
mContext, intentCreationResult, mUserId);
Intent intent = intentCreationResult.getIntent();
intent.setAction(UUID.randomUUID().toString());
+ if (Flags.frameworkSessionIdMetricBundle()) {
+ intent.putExtra(SESSION_ID_TRACK_ONE,
+ requestSessionMetric.getInitialPhaseMetric().getSessionIdCaller());
+ intent.putExtra(SESSION_ID_TRACK_TWO, requestSessionMetric.getSessionIdTrackTwo());
+ }
//TODO: Create unique pending intent using request code and cancel any pre-existing pending
// intents
return PendingIntent.getActivityAsUser(
@@ -192,8 +204,8 @@
* each autofill id and passed in as extras in the pending intent set as authentication
* of the pinned entry.
*
- * @param requestInfo the information about the request
- * @param requestSessionMetric the metric object for logging
+ * @param requestInfo the information about the request
+ * @param requestSessionMetric the metric object for logging
*/
public Intent createIntentForAutofill(RequestInfo requestInfo,
RequestSessionMetric requestSessionMetric) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ac1219c..2627895 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -26,6 +26,7 @@
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AIRPLANE_MODE;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_FUNCTIONS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIO_OUTPUT;
@@ -126,6 +127,7 @@
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
import static android.app.admin.DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED;
+import static android.app.admin.DevicePolicyManager.APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY;
import static android.app.admin.DevicePolicyManager.CONTENT_PROTECTION_DISABLED;
import static android.app.admin.DevicePolicyManager.ContentProtectionPolicy;
import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
@@ -332,6 +334,7 @@
import android.app.admin.DevicePolicyDrawableResource;
import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.AppFunctionsPolicy;
import android.app.admin.DevicePolicyManager.DeviceOwnerType;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
import android.app.admin.DevicePolicyManager.OperationSafetyReason;
@@ -9085,11 +9088,13 @@
}
CallerIdentity caller = getCallerIdentity(who);
- if (!Flags.setAutoTimeEnabledCoexistence()) {
+ if (Flags.setAutoTimeEnabledCoexistence()) {
+ Preconditions.checkCallAuthorization(hasPermission(SET_TIME, callerPackageName));
+ } else {
Objects.requireNonNull(who, "ComponentName is null");
- }
- Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
+ Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
|| isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller));
+ }
return mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) > 0;
}
@@ -9147,7 +9152,7 @@
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
/* who */ null,
SET_TIME,
- caller.getPackageName(),
+ callerPackageName,
UserHandle.USER_ALL
);
Integer state = mDevicePolicyEngine.getGlobalPolicySetByAdmin(
@@ -9166,10 +9171,7 @@
}
CallerIdentity caller = getCallerIdentity(who);
-
- if (!Flags.setAutoTimeZoneEnabledCoexistence()) {
- Objects.requireNonNull(who, "ComponentName is null");
- }
+ Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
|| isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(
caller));
@@ -9193,10 +9195,15 @@
}
CallerIdentity caller = getCallerIdentity(who);
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
+ if (Flags.setAutoTimeZoneEnabledCoexistence()) {
+ Preconditions.checkCallAuthorization(
+ hasPermission(SET_TIME_ZONE, callerPackageName));
+ } else {
+ Objects.requireNonNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
|| isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(
caller));
+ }
return mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) > 0;
}
@@ -9248,7 +9255,7 @@
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
/* who */ null,
SET_TIME_ZONE,
- caller.getPackageName(),
+ callerPackageName,
UserHandle.USER_ALL
);
Integer state = mDevicePolicyEngine.getGlobalPolicySetByAdmin(
@@ -22970,6 +22977,7 @@
MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
MANAGE_DEVICE_POLICY_APPS_CONTROL,
+ MANAGE_DEVICE_POLICY_APP_FUNCTIONS,
MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
MANAGE_DEVICE_POLICY_AUDIO_OUTPUT,
MANAGE_DEVICE_POLICY_AUTOFILL,
@@ -23057,6 +23065,7 @@
MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
MANAGE_DEVICE_POLICY_APPS_CONTROL,
+ MANAGE_DEVICE_POLICY_APP_FUNCTIONS,
MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
MANAGE_DEVICE_POLICY_AUDIO_OUTPUT,
MANAGE_DEVICE_POLICY_AUTOFILL,
@@ -23306,6 +23315,8 @@
MANAGE_DEVICE_POLICY_ACROSS_USERS);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_CONTENT_PROTECTION,
MANAGE_DEVICE_POLICY_ACROSS_USERS);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_APP_FUNCTIONS,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS);
// These permissions may grant access to user data and therefore must be protected with
// MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL for cross-user calls.
@@ -23971,6 +23982,47 @@
}
}
+ @Override
+ public void setAppFunctionsPolicy(String callerPackageName, @AppFunctionsPolicy int policy) {
+ if (!android.app.appfunctions.flags.Flags.enableAppFunctionManager()) {
+ return;
+ }
+
+ CallerIdentity caller = getCallerIdentity(callerPackageName);
+ int userId = caller.getUserId();
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_APP_FUNCTIONS_POLICY);
+ EnforcingAdmin enforcingAdmin =
+ enforcePermissionAndGetEnforcingAdmin(
+ /* who */null, MANAGE_DEVICE_POLICY_APP_FUNCTIONS,
+ callerPackageName, userId);
+
+ if (policy == APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY) {
+ mDevicePolicyEngine.removeLocalPolicy(
+ PolicyDefinition.APP_FUNCTIONS, enforcingAdmin, userId);
+ } else {
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.APP_FUNCTIONS,
+ enforcingAdmin, new IntegerPolicyValue(policy),
+ userId);
+ }
+ }
+
+ @Override
+ public @AppFunctionsPolicy int getAppFunctionsPolicy(String callerPackageName, int userId) {
+ if (!android.app.appfunctions.flags.Flags.enableAppFunctionManager()) {
+ return APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY;
+ }
+
+ CallerIdentity caller = getCallerIdentity(callerPackageName);
+ enforceCanQuery(MANAGE_DEVICE_POLICY_APP_FUNCTIONS, callerPackageName, userId);
+ Integer policy =
+ mDevicePolicyEngine.getResolvedPolicy(PolicyDefinition.APP_FUNCTIONS, userId);
+ if (policy == null) {
+ return APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY;
+ }
+ return policy;
+ }
+
private void updateContentProtectionPolicyCache(@UserIdInt int userId) {
mPolicyCache.setContentProtectionPolicy(
userId,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 24b16b7..543e32f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -318,6 +318,20 @@
PolicyEnforcerCallbacks::setContentProtectionPolicy,
new IntegerPolicySerializer());
+ static PolicyDefinition<Integer> APP_FUNCTIONS = new PolicyDefinition<>(
+ new NoArgsPolicyKey(DevicePolicyIdentifiers.APP_FUNCTIONS_POLICY),
+ new MostRestrictive<>(
+ List.of(
+ new IntegerPolicyValue(
+ DevicePolicyManager.APP_FUNCTIONS_DISABLED),
+ new IntegerPolicyValue(
+ DevicePolicyManager.APP_FUNCTIONS_DISABLED_CROSS_PROFILE),
+ new IntegerPolicyValue(
+ DevicePolicyManager.APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY))),
+ POLICY_FLAG_LOCAL_ONLY_POLICY,
+ PolicyEnforcerCallbacks::noOp,
+ new IntegerPolicySerializer());
+
static PolicyDefinition<Integer> PASSWORD_COMPLEXITY = new PolicyDefinition<>(
new NoArgsPolicyKey(DevicePolicyIdentifiers.PASSWORD_COMPLEXITY_POLICY),
new MostRestrictive<>(
@@ -398,6 +412,8 @@
USB_DATA_SIGNALING);
POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.CONTENT_PROTECTION_POLICY,
CONTENT_PROTECTION);
+ POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.APP_FUNCTIONS_POLICY,
+ APP_FUNCTIONS);
// Intentionally not flagged since if the flag is flipped off on a device already
// having PASSWORD_COMPLEXITY policy in the on-device XML, it will cause the
// deserialization logic to break due to seeing an unknown tag.
@@ -656,10 +672,6 @@
}
}
- void saveToXml(TypedXmlSerializer serializer) throws IOException {
- mPolicyKey.saveToXml(serializer);
- }
-
@Nullable
static <V> PolicyDefinition<V> readFromXml(TypedXmlPullParser parser)
throws XmlPullParserException, IOException {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
index a4fa089..0d9dbaa 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.admin.PolicyValue;
-import android.app.admin.flags.Flags;
import android.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
@@ -41,7 +40,6 @@
private static final String TAG = "PolicyState";
private static final String TAG_ADMIN_POLICY_ENTRY = "admin-policy-entry";
- private static final String TAG_POLICY_DEFINITION_ENTRY = "policy-definition-entry";
private static final String TAG_RESOLVED_VALUE_ENTRY = "resolved-value-entry";
private static final String TAG_ENFORCING_ADMIN_ENTRY = "enforcing-admin-entry";
private static final String TAG_POLICY_VALUE_ENTRY = "policy-value-entry";
@@ -225,12 +223,6 @@
}
void saveToXml(TypedXmlSerializer serializer) throws IOException {
- if (!Flags.dontWritePolicyDefinition()) {
- serializer.startTag(/* namespace= */ null, TAG_POLICY_DEFINITION_ENTRY);
- mPolicyDefinition.saveToXml(serializer);
- serializer.endTag(/* namespace= */ null, TAG_POLICY_DEFINITION_ENTRY);
- }
-
if (mCurrentResolvedPolicy != null) {
serializer.startTag(/* namespace= */ null, TAG_RESOLVED_VALUE_ENTRY);
mPolicyDefinition.savePolicyValueToXml(
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index aa63c4a..92dbf63 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -201,7 +201,6 @@
import com.android.server.notification.NotificationManagerService;
import com.android.server.oemlock.OemLockService;
import com.android.server.om.OverlayManagerService;
-import com.android.server.ondeviceintelligence.OnDeviceIntelligenceManagerService;
import com.android.server.os.BugreportManagerService;
import com.android.server.os.DeviceIdentifiersPolicyService;
import com.android.server.os.NativeTombstoneManagerService;
@@ -392,6 +391,8 @@
"com.android.server.sdksandbox.SdkSandboxManagerService$Lifecycle";
private static final String AD_SERVICES_MANAGER_SERVICE_CLASS =
"com.android.server.adservices.AdServicesManagerService$Lifecycle";
+ private static final String ON_DEVICE_INTELLIGENCE_MANAGER_SERVICE_CLASS =
+ "com.android.server.ondeviceintelligence.OnDeviceIntelligenceManagerService";
private static final String ON_DEVICE_PERSONALIZATION_SYSTEM_SERVICE_CLASS =
"com.android.server.ondevicepersonalization."
+ "OnDevicePersonalizationSystemService$Lifecycle";
@@ -2932,7 +2933,7 @@
t.traceEnd();
// UprobeStats DynamicInstrumentationManager
- if (com.android.art.flags.Flags.executableMethodFileOffsets()) {
+ if (android.uprobestats.flags.Flags.executableMethodFileOffsets()) {
t.traceBegin("StartDynamicInstrumentationManager");
mSystemServiceManager.startService(DynamicInstrumentationManagerService.class);
t.traceEnd();
@@ -3453,7 +3454,7 @@
private void startOnDeviceIntelligenceService(TimingsTraceAndSlog t) {
t.traceBegin("startOnDeviceIntelligenceManagerService");
- mSystemServiceManager.startService(OnDeviceIntelligenceManagerService.class);
+ mSystemServiceManager.startService(ON_DEVICE_INTELLIGENCE_MANAGER_SERVICE_CLASS);
t.traceEnd();
}
diff --git a/services/java/com/android/server/flags.aconfig b/services/java/com/android/server/flags.aconfig
index 4412968..0d222fb 100644
--- a/services/java/com/android/server/flags.aconfig
+++ b/services/java/com/android/server/flags.aconfig
@@ -10,14 +10,6 @@
}
flag {
- name: "remove_java_service_manager_cache"
- namespace: "system_performance"
- description: "This flag turns off Java's Service Manager caching mechanism."
- bug: "333854840"
- is_fixed_read_only: true
-}
-
-flag {
name: "remove_text_service"
namespace: "wear_frameworks"
description: "Remove TextServiceManagerService on Wear"
diff --git a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
index f5360eb..6b28047 100644
--- a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
@@ -213,7 +213,8 @@
}
private int getShareEventType(IntentFilter intentFilter) {
- String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null;
+ String mimeType = (intentFilter != null && intentFilter.countDataTypes() > 0)
+ ? intentFilter.getDataType(0) : null;
return getDataManager().mimeTypeToShareEventType(mimeType);
}
diff --git a/services/proguard.flags b/services/proguard.flags
index 977bd19..0e1f68e 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -44,6 +44,9 @@
-keep,allowoptimization,allowaccessmodification class com.android.server.input.NativeInputManagerService$NativeImpl { *; }
-keep,allowoptimization,allowaccessmodification class com.android.server.ThreadPriorityBooster { *; }
+# allow invoking start-service using class name in both apex and services jar.
+-keep,allowoptimization,allowaccessmodification class com.android.server.ondeviceintelligence.OnDeviceIntelligenceManagerService { *; }
+
# Keep all aconfig Flag class as they might be statically referenced by other packages
# An merge or inlining could lead to missing dependencies that cause run time errors
-keepclassmembernames class android.**.Flags, com.android.**.Flags { public *; }
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 02e0bbf..eb61a40 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -30,13 +30,10 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
import static org.testng.Assert.expectThrows;
import android.app.backup.BackupManager;
@@ -90,7 +87,6 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
/**
@@ -110,7 +106,6 @@
private static final String TAG = "BMSTest";
private static final String PACKAGE_1 = "some.package.1";
private static final String PACKAGE_2 = "some.package.2";
- private static final String USER_FACING_PACKAGE = "user.facing.package";
private static final int USER_ID = 10;
@Mock private TransportManager mTransportManager;
@@ -1213,47 +1208,6 @@
eq(packageTrackingReceiver), eq(UserHandle.of(USER_ID)), any(), any(), any());
}
- @Test
- public void testFilterUserFacingPackages_shouldSkipUserFacing_filtersUserFacing() {
- List<PackageInfo> packages = Arrays.asList(getPackageInfo(USER_FACING_PACKAGE),
- getPackageInfo(PACKAGE_1));
- UserBackupManagerService backupManagerService = spy(
- createUserBackupManagerServiceAndRunTasks());
- when(backupManagerService.shouldSkipUserFacingData()).thenReturn(true);
- when(backupManagerService.shouldSkipPackage(eq(USER_FACING_PACKAGE))).thenReturn(true);
-
- List<PackageInfo> filteredPackages = backupManagerService.filterUserFacingPackages(
- packages);
-
- assertFalse(containsPackage(filteredPackages, USER_FACING_PACKAGE));
- assertTrue(containsPackage(filteredPackages, PACKAGE_1));
- }
-
- @Test
- public void testFilterUserFacingPackages_shouldNotSkipUserFacing_doesNotFilterUserFacing() {
- List<PackageInfo> packages = Arrays.asList(getPackageInfo(USER_FACING_PACKAGE),
- getPackageInfo(PACKAGE_1));
- UserBackupManagerService backupManagerService = spy(
- createUserBackupManagerServiceAndRunTasks());
- when(backupManagerService.shouldSkipUserFacingData()).thenReturn(false);
- when(backupManagerService.shouldSkipPackage(eq(USER_FACING_PACKAGE))).thenReturn(true);
-
- List<PackageInfo> filteredPackages = backupManagerService.filterUserFacingPackages(
- packages);
-
- assertTrue(containsPackage(filteredPackages, USER_FACING_PACKAGE));
- assertTrue(containsPackage(filteredPackages, PACKAGE_1));
- }
-
- private static boolean containsPackage(List<PackageInfo> packages, String targetPackage) {
- for (PackageInfo packageInfo : packages) {
- if (targetPackage.equals(packageInfo.packageName)) {
- return true;
- }
- }
- return false;
- }
-
private UserBackupManagerService createUserBackupManagerServiceAndRunTasks() {
return BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks(
USER_ID, mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java
index 3093c42..0ccaa60 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionService.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java
@@ -29,11 +29,13 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import android.os.UserHandle;
import android.util.SparseArray;
import com.android.internal.R;
@@ -63,11 +65,13 @@
private final SparseArray<SupervisionUserData> mUserData = new SparseArray<>();
private final DevicePolicyManagerInternal mDpmInternal;
+ private final PackageManager mPackageManager;
private final UserManagerInternal mUserManagerInternal;
public SupervisionService(Context context) {
mContext = context.createAttributionContext(LOG_TAG);
mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
+ mPackageManager = context.getPackageManager();
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
}
@@ -148,12 +152,13 @@
/** Returns whether the supervision app has profile owner status. */
private boolean isProfileOwner(@UserIdInt int userId) {
ComponentName profileOwner = mDpmInternal.getProfileOwnerAsUser(userId);
- if (profileOwner == null) {
- return false;
- }
+ return profileOwner != null && isSupervisionAppPackage(profileOwner.getPackageName());
+ }
- String configPackage = mContext.getResources().getString(R.string.config_systemSupervision);
- return profileOwner.getPackageName().equals(configPackage);
+ /** Returns whether the given package name belongs to the supervision role holder. */
+ private boolean isSupervisionAppPackage(String packageName) {
+ return packageName.equals(
+ mContext.getResources().getString(R.string.config_systemSupervision));
}
public static class Lifecycle extends SystemService {
@@ -211,6 +216,21 @@
private final class SupervisionManagerInternalImpl extends SupervisionManagerInternal {
@Override
+ public boolean isActiveSupervisionApp(int uid) {
+ String[] packages = mPackageManager.getPackagesForUid(uid);
+ if (packages == null) {
+ return false;
+ }
+ for (var packageName : packages) {
+ if (SupervisionService.this.isSupervisionAppPackage(packageName)) {
+ int userId = UserHandle.getUserId(uid);
+ return SupervisionService.this.isSupervisionEnabledForUser(userId);
+ }
+ }
+ return false;
+ }
+
+ @Override
public boolean isSupervisionEnabledForUser(@UserIdInt int userId) {
return SupervisionService.this.isSupervisionEnabledForUser(userId);
}
diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/os/instrumentation/ParseMethodDescriptorTest.java b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/os/instrumentation/ParseMethodDescriptorTest.java
index 5492ba6..6e14bad 100644
--- a/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/os/instrumentation/ParseMethodDescriptorTest.java
+++ b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/os/instrumentation/ParseMethodDescriptorTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertThrows;
import android.os.instrumentation.MethodDescriptor;
+import android.os.instrumentation.MethodDescriptorParser;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -37,7 +38,7 @@
/**
* Test class for
- * {@link DynamicInstrumentationManagerService#parseMethodDescriptor(ClassLoader,
+ * {@link MethodDescriptorParser#parseMethodDescriptor(ClassLoader,
* MethodDescriptor)}.
* <p>
* Build/Install/Run:
@@ -119,13 +120,13 @@
}
private Method parseMethodDescriptor(String fqcn, String methodName) {
- return DynamicInstrumentationManagerService.parseMethodDescriptor(
+ return MethodDescriptorParser.parseMethodDescriptor(
getClass().getClassLoader(),
getMethodDescriptor(fqcn, methodName, new String[]{}));
}
private Method parseMethodDescriptor(String fqcn, String methodName, String[] fqParameters) {
- return DynamicInstrumentationManagerService.parseMethodDescriptor(
+ return MethodDescriptorParser.parseMethodDescriptor(
getClass().getClassLoader(),
getMethodDescriptor(fqcn, methodName, fqParameters));
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
index 66e9c98..238654d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -126,16 +127,11 @@
@Test
public void testConstructor_doesNotStartsLightSensorController() {
- verify(mMockLightSensorController, never()).restart();
- }
-
- @Test
- public void testConstructor_startsLightSensorController() {
when(mMockModifier.shouldListenToLightSensor()).thenReturn(true);
mClamperController = createBrightnessClamperController();
- verify(mMockLightSensorController).restart();
+ verify(mMockLightSensorController, never()).restart();
}
@Test
@@ -171,20 +167,43 @@
@Test
public void testOnDisplayChanged_doesNotRestartLightSensor() {
+ mClamperController.clamp(mDisplayBrightnessState, mMockRequest, 0.1f,
+ false, STATE_ON);
+ reset(mMockLightSensorController);
+
mClamperController.onDisplayChanged(mMockDisplayDeviceData);
verify(mMockLightSensorController, never()).restart();
+ verify(mMockLightSensorController).stop();
}
@Test
public void testOnDisplayChanged_restartsLightSensor() {
when(mMockModifier.shouldListenToLightSensor()).thenReturn(true);
+ mClamperController.clamp(mDisplayBrightnessState, mMockRequest, 0.1f,
+ false, STATE_ON);
+ reset(mMockLightSensorController);
+
mClamperController.onDisplayChanged(mMockDisplayDeviceData);
+ verify(mMockLightSensorController, never()).stop();
verify(mMockLightSensorController).restart();
}
@Test
+ public void testOnDisplayChanged_doesNotRestartLightSensor_screenOff() {
+ when(mMockModifier.shouldListenToLightSensor()).thenReturn(true);
+ mClamperController.clamp(mDisplayBrightnessState, mMockRequest, 0.1f,
+ false, STATE_OFF);
+ reset(mMockLightSensorController);
+
+ mClamperController.onDisplayChanged(mMockDisplayDeviceData);
+
+ verify(mMockLightSensorController, never()).restart();
+ verify(mMockLightSensorController).stop();
+ }
+
+ @Test
public void testClamp_AppliesModifier() {
float initialBrightness = 0.2f;
boolean initialSlowChange = true;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
index d58e772..835705d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
@@ -188,8 +188,8 @@
}
@Test
- public void testDensityBasedCoarsening_ifFeatureIsEnabledButNotDefault_cacheIsNotUsed() {
- mSetFlagsRule.disableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+ public void testDensityBasedCoarsening_ifFeatureIsEnabledButNoDefaultValue_cacheIsNotUsed() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
LocationFudgerCache cache = mock(LocationFudgerCache.class);
doReturn(false).when(cache).hasDefaultValue();
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
index de029e0..90e1263 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
@@ -56,8 +56,8 @@
testHandler.flush()
verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper).sendDistractingPackagesChanged(any(Computer::class.java),
- pkgListCaptor.capture(), any(), any(), flagsCaptor.capture())
+ verify(broadcastHelper).sendDistractingPackagesChanged(
+ any(), pkgListCaptor.capture(), any(), any(), flagsCaptor.capture())
val modifiedPackages = pkgListCaptor.value
val distractionFlags = flagsCaptor.value
@@ -158,8 +158,7 @@
verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
verify(broadcastHelper).sendDistractingPackagesChanged(
- any(Computer::class.java), pkgListCaptor.capture(), any(), eq(TEST_USER_ID),
- flagsCaptor.capture())
+ any(), pkgListCaptor.capture(), any(), eq(TEST_USER_ID), flagsCaptor.capture())
val modifiedPackages = pkgListCaptor.value
val distractionFlags = flagsCaptor.value
assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
@@ -198,12 +197,12 @@
@Test
fun sendDistractingPackagesChanged() {
- broadcastHelper.sendDistractingPackagesChanged(pms.snapshotComputer(),
+ broadcastHelper.sendDistractingPackagesChanged(pms::snapshotComputer,
packagesToChange, uidsToChange, TEST_USER_ID,
PackageManager.RESTRICTION_HIDE_NOTIFICATIONS)
testHandler.flush()
- verify(broadcastHelper).sendDistractingPackagesChanged(any(Computer::class.java),
- pkgListCaptor.capture(), uidsCaptor.capture(), eq(TEST_USER_ID), any())
+ verify(broadcastHelper).sendDistractingPackagesChanged(
+ any(), pkgListCaptor.capture(), uidsCaptor.capture(), eq(TEST_USER_ID), any())
var changedPackages = pkgListCaptor.value
var changedUids = uidsCaptor.value
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
index 7444403..60e8250 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
@@ -58,8 +58,8 @@
testHandler.flush()
verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(any(Computer::class.java),
- eq(Intent.ACTION_PACKAGES_SUSPENDED), pkgListCaptor.capture(), any(), any(), any())
+ verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(any(),
+ eq(Intent.ACTION_PACKAGES_SUSPENDED), pkgListCaptor.capture(), any(), any(), any())
var modifiedPackages = pkgListCaptor.value
assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
@@ -149,11 +149,11 @@
testHandler.flush()
verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(any(Computer::class.java),
- eq(Intent.ACTION_PACKAGES_UNSUSPENDED), pkgListCaptor.capture(), any(), any(),
- any())
- verify(broadcastHelper).sendMyPackageSuspendedOrUnsuspended(any(Computer::class.java),
- any(), any(), any())
+ verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(
+ any(), eq(Intent.ACTION_PACKAGES_UNSUSPENDED),
+ pkgListCaptor.capture(), any(), any(), any())
+ verify(broadcastHelper).sendMyPackageSuspendedOrUnsuspended(
+ any(), any(), any(), any())
var modifiedPackages = pkgListCaptor.value
assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
@@ -230,10 +230,10 @@
testHandler.flush()
verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(any(Computer::class.java),
- eq(Intent.ACTION_PACKAGES_UNSUSPENDED), any(), any(), any(), any())
- verify(broadcastHelper).sendMyPackageSuspendedOrUnsuspended(any(Computer::class.java),
- any(), any(), any())
+ verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(
+ any(), eq(Intent.ACTION_PACKAGES_UNSUSPENDED), any(), any(), any(), any())
+ verify(broadcastHelper).sendMyPackageSuspendedOrUnsuspended(
+ any(), any(), any(), any())
assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNull()
diff --git a/services/tests/ondeviceintelligencetests/Android.bp b/services/tests/ondeviceintelligencetests/Android.bp
index a31a3fb..f235da2 100644
--- a/services/tests/ondeviceintelligencetests/Android.bp
+++ b/services/tests/ondeviceintelligencetests/Android.bp
@@ -44,14 +44,18 @@
"truth",
"frameworks-base-testutils",
"androidx.test.rules",
- ],
+ ] + select(soong_config_variable("ANDROID", "release_ondevice_intelligence_module"), {
+ "true": [
+ "service-ondeviceintelligence.impl",
+ ],
+ default: [],
+ }),
libs: [
"android.test.mock.stubs.system",
"android.test.base.stubs.system",
"android.test.runner.stubs.system",
],
-
certificate: "platform",
platform_apis: true,
test_suites: ["device-tests"],
diff --git a/services/tests/ondeviceintelligencetests/OWNERS b/services/tests/ondeviceintelligencetests/OWNERS
index 09774f7..a4fc758 100644
--- a/services/tests/ondeviceintelligencetests/OWNERS
+++ b/services/tests/ondeviceintelligencetests/OWNERS
@@ -1 +1,3 @@
-file:/core/java/android/app/ondeviceintelligence/OWNERS
+shiqing@google.com
+sandeepbandaru@google.com
+shivanker@google.com
diff --git a/services/tests/ondeviceintelligencetests/src/com/android/server/ondeviceintelligence/InferenceInfoStoreTest.java b/services/tests/ondeviceintelligencetests/src/com/android/server/ondeviceintelligence/InferenceInfoStoreTest.java
index d12579c..28ccb84 100644
--- a/services/tests/ondeviceintelligencetests/src/com/android/server/ondeviceintelligence/InferenceInfoStoreTest.java
+++ b/services/tests/ondeviceintelligencetests/src/com/android/server/ondeviceintelligence/InferenceInfoStoreTest.java
@@ -21,7 +21,6 @@
import android.app.ondeviceintelligence.InferenceInfo;
import android.os.Bundle;
import android.os.PersistableBundle;
-import android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService;
import android.util.Base64;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -38,6 +37,8 @@
public class InferenceInfoStoreTest {
InferenceInfoStore inferenceInfoStore;
+ public static final String INFERENCE_INFO_BUNDLE_KEY = "inference_info";
+
@Before
public void setUp() {
inferenceInfoStore = new InferenceInfoStore(1000);
@@ -46,7 +47,7 @@
@Test
public void testInferenceInfoParsesFromBundleSuccessfully() throws Exception {
Bundle bundle = new Bundle();
- bundle.putByteArray(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY,
+ bundle.putByteArray(INFERENCE_INFO_BUNDLE_KEY,
getInferenceInfoBytes(1, 1, 100));
inferenceInfoStore.addInferenceInfoFromBundle(bundle);
List<InferenceInfo> inferenceInfos = inferenceInfoStore.getLatestInferenceInfo(0);
@@ -59,7 +60,7 @@
@Test
public void testInferenceInfoParsesFromPersistableBundleSuccessfully() throws Exception {
PersistableBundle bundle = new PersistableBundle();
- bundle.putString(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY,
+ bundle.putString(INFERENCE_INFO_BUNDLE_KEY,
Base64.encodeToString(getInferenceInfoBytes(1, 1, 100), Base64.DEFAULT));
inferenceInfoStore.addInferenceInfoFromBundle(bundle);
List<InferenceInfo> inferenceInfos = inferenceInfoStore.getLatestInferenceInfo(0);
@@ -74,11 +75,11 @@
public void testEvictionAfterMaxAge() throws Exception {
PersistableBundle bundle = new PersistableBundle();
long testStartTime = System.currentTimeMillis();
- bundle.putString(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY,
+ bundle.putString(INFERENCE_INFO_BUNDLE_KEY,
Base64.encodeToString(getInferenceInfoBytes(1, testStartTime - 10,
testStartTime + 100), Base64.DEFAULT));
inferenceInfoStore.addInferenceInfoFromBundle(bundle);
- bundle.putString(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY,
+ bundle.putString(INFERENCE_INFO_BUNDLE_KEY,
Base64.encodeToString(getInferenceInfoBytes(1, testStartTime - 5,
testStartTime + 100), Base64.DEFAULT));
inferenceInfoStore.addInferenceInfoFromBundle(bundle);
diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
index a9b4ca1..7248833 100644
--- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -182,12 +182,6 @@
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.category = ApplicationInfo.CATEGORY_GAME;
mSupportInfo = new SupportInfo();
- mSupportInfo.usesSessions = true;
- mSupportInfo.sessionHints = 5;
- mSupportInfo.sessionModes = 1;
- mSupportInfo.modes = 3;
- mSupportInfo.boosts = 3;
- mSupportInfo.sessionTags = 63;
mSupportInfo.headroom = new SupportInfo.HeadroomSupportInfo();
mSupportInfo.headroom.isCpuSupported = true;
mSupportInfo.headroom.cpuMinIntervalMillis = 2000;
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 d7b60cf..2b15231 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
@@ -39,7 +39,6 @@
import android.os.Handler;
import android.os.Looper;
import android.os.connectivity.WifiActivityEnergyInfo;
-import android.platform.test.ravenwood.RavenwoodConfig;
import android.power.PowerStatsInternal;
import android.util.IntArray;
import android.util.SparseArray;
@@ -66,9 +65,6 @@
@SuppressWarnings("GuardedBy")
@android.platform.test.annotations.DisabledOnRavenwood
public class BatteryExternalStatsWorkerTest {
- @RavenwoodConfig.Config
- public final RavenwoodConfig mRavenwood = new RavenwoodConfig.Builder().build();
-
private BatteryExternalStatsWorker mBatteryExternalStatsWorker;
private TestPowerStatsInternal mPowerStatsInternal;
private Handler mHandler;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java
index f64dc08..ed3cda0 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java
@@ -34,7 +34,6 @@
import android.os.BatteryConsumer;
import android.os.PersistableBundle;
import android.os.Process;
-import android.platform.test.ravenwood.RavenwoodConfig;
import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.MonotonicClock;
@@ -48,11 +47,6 @@
import org.junit.Test;
public class WakelockPowerStatsProcessorTest {
- @RavenwoodConfig.Config
- public static final RavenwoodConfig sConfig = new RavenwoodConfig.Builder()
- .setProvideMainThread(true)
- .build();
-
@Rule
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
.setAveragePower(PowerProfile.POWER_CPU_IDLE, 720);
diff --git a/services/tests/security/intrusiondetection/AndroidManifest.xml b/services/tests/security/intrusiondetection/AndroidManifest.xml
index b30710d..d58e0d8 100644
--- a/services/tests/security/intrusiondetection/AndroidManifest.xml
+++ b/services/tests/security/intrusiondetection/AndroidManifest.xml
@@ -19,18 +19,10 @@
<uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING" />
<uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE" />
<application android:testOnly="true" android:debuggable="true" android:usesCleartextTraffic="true">
<uses-library android:name="android.test.runner"/>
- <receiver android:name="com.android.server.security.intrusiondetection.IntrusionDetectionAdminReceiver"
- android:permission="android.permission.BIND_DEVICE_ADMIN"
- android:exported="true">
- <meta-data android:name="android.app.device_admin"
- android:resource="@xml/device_admin"/>
- <intent-filter>
- <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
- </intent-filter>
- </receiver>
</application>
<queries>
diff --git a/services/tests/security/intrusiondetection/AndroidTest.xml b/services/tests/security/intrusiondetection/AndroidTest.xml
index 6489dea4a..0d21158 100644
--- a/services/tests/security/intrusiondetection/AndroidTest.xml
+++ b/services/tests/security/intrusiondetection/AndroidTest.xml
@@ -24,6 +24,10 @@
<option name="install-arg" value="-t" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="setprop intrusiondetection_service_name com.android.coretests.apps.testapp/.TestLoggingService" />
+ </target_preparer>
+
<option name="test-tag" value="IntrusionDetectionServiceTests" />
<test class="com.android.tradefed.testtype.InstrumentationTest" >
<option name="package" value="com.android.server.security.intrusiondetection.tests" />
diff --git a/services/tests/security/intrusiondetection/res/xml/device_admin.xml b/services/tests/security/intrusiondetection/res/xml/device_admin.xml
deleted file mode 100644
index f8cd8f0..0000000
--- a/services/tests/security/intrusiondetection/res/xml/device_admin.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?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.
--->
-
-<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
-</device-admin>
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
index e505ebe..5cba6b2 100644
--- a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
@@ -16,12 +16,12 @@
package com.android.server.security.intrusiondetection;
+import static android.Manifest.permission.BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE;
import static android.Manifest.permission.INTERNET;
import static android.Manifest.permission.MANAGE_INTRUSION_DETECTION_STATE;
import static android.Manifest.permission.READ_INTRUSION_DETECTION_STATE;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
@@ -45,7 +45,6 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
-import android.os.Bundle;
import android.os.Looper;
import android.os.PermissionEnforcer;
import android.os.RemoteException;
@@ -61,17 +60,13 @@
import androidx.test.core.app.ApplicationProvider;
import com.android.bedstead.harrier.BedsteadJUnit4;
-import com.android.bedstead.harrier.annotations.AfterClass;
-import com.android.bedstead.harrier.annotations.BeforeClass;
import com.android.bedstead.multiuser.annotations.RequireRunOnSystemUser;
import com.android.bedstead.nene.TestApis;
import com.android.bedstead.nene.devicepolicy.DeviceOwner;
-import com.android.bedstead.nene.exceptions.NeneException;
import com.android.bedstead.permissions.CommonPermissions;
import com.android.bedstead.permissions.PermissionContext;
import com.android.bedstead.permissions.annotations.EnsureHasPermission;
import com.android.coretests.apps.testapp.LocalIntrusionDetectionEventTransport;
-import com.android.internal.infra.AndroidFuture;
import com.android.server.ServiceThread;
import org.junit.Before;
@@ -129,28 +124,6 @@
"com.android.coretests.apps.testapp";
private static final String TEST_SERVICE = TEST_PKG + ".TestLoggingService";
- @BeforeClass
- public static void setDeviceOwner() {
- ComponentName admin =
- new ComponentName(
- ApplicationProvider.getApplicationContext(),
- IntrusionDetectionAdminReceiver.class);
- try {
- sDeviceOwner = TestApis.devicePolicy().setDeviceOwner(admin);
- } catch (NeneException e) {
- fail("Failed to set device owner " + admin.flattenToString() + ": " + e);
- }
- }
-
- @AfterClass
- public static void removeDeviceOwner() {
- try {
- sDeviceOwner.remove();
- } catch (NeneException e) {
- fail("Failed to remove device owner : " + e);
- }
- }
-
@SuppressLint("VisibleForTests")
@Before
public void setUp() throws Exception {
@@ -159,6 +132,7 @@
mPermissionEnforcer = new FakePermissionEnforcer();
mPermissionEnforcer.grant(READ_INTRUSION_DETECTION_STATE);
mPermissionEnforcer.grant(MANAGE_INTRUSION_DETECTION_STATE);
+ mPermissionEnforcer.grant(BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE);
mTestLooper = new TestLooper();
mLooper = mTestLooper.getLooper();
@@ -178,6 +152,7 @@
}
@Test
+ @EnsureHasPermission(CommonPermissions.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
public void testRemoveStateCallback_NoPermission() {
mPermissionEnforcer.revoke(READ_INTRUSION_DETECTION_STATE);
StateCallback scb = new StateCallback();
@@ -229,6 +204,7 @@
}
@Test
+ @Ignore("Unit test does not run as system service UID")
public void testRemoveStateCallback() throws RemoteException {
mIntrusionDetectionService.setState(STATE_DISABLED);
StateCallback scb1 = new StateCallback();
@@ -239,7 +215,6 @@
assertEquals(STATE_DISABLED, scb1.mState);
assertEquals(STATE_DISABLED, scb2.mState);
- doReturn(true).when(mDataAggregator).initialize();
doReturn(true).when(mIntrusionDetectionEventTransportConnection).initialize();
mIntrusionDetectionService.getBinderService().removeStateCallback(scb2);
@@ -252,6 +227,7 @@
assertNull(ccb.mErrorCode);
}
+ @Ignore("Unit test does not run as system service UID")
@Test
public void testEnable_FromDisabled_TwoStateCallbacks() throws RemoteException {
mIntrusionDetectionService.setState(STATE_DISABLED);
@@ -412,39 +388,13 @@
}
@Test
- @RequireRunOnSystemUser
- public void testDataSources_Initialize_HasDeviceOwner() throws Exception {
- NetworkLogSource networkLogSource = new NetworkLogSource(mContext, mDataAggregator);
- SecurityLogSource securityLogSource = new SecurityLogSource(mContext, mDataAggregator);
-
- assertTrue(networkLogSource.initialize());
- assertTrue(securityLogSource.initialize());
- }
-
- @Test
- @RequireRunOnSystemUser
- public void testDataSources_Initialize_NoDeviceOwner() throws Exception {
- NetworkLogSource networkLogSource = new NetworkLogSource(mContext, mDataAggregator);
- SecurityLogSource securityLogSource = new SecurityLogSource(mContext, mDataAggregator);
- ComponentName admin = sDeviceOwner.componentName();
-
- try {
- sDeviceOwner.remove();
- assertFalse(networkLogSource.initialize());
- assertFalse(securityLogSource.initialize());
- } finally {
- sDeviceOwner = TestApis.devicePolicy().setDeviceOwner(admin);
- }
- }
-
- @Test
+ @Ignore("Unit test does not run as system service UID")
@RequireRunOnSystemUser
@EnsureHasPermission(CommonPermissions.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
public void testDataAggregator_AddSecurityEvent() throws Exception {
mIntrusionDetectionService.setState(STATE_ENABLED);
ServiceThread mockThread = spy(ServiceThread.class);
mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread);
- assertTrue(mDataAggregator.initialize());
// SecurityLogging generates a number of events and callbacks, so create a latch to wait for
// the given event.
@@ -488,12 +438,12 @@
@Test
@RequireRunOnSystemUser
+ @Ignore("Unit test does not run as system service UID")
@EnsureHasPermission(CommonPermissions.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
public void testDataAggregator_AddNetworkEvent() throws Exception {
mIntrusionDetectionService.setState(STATE_ENABLED);
ServiceThread mockThread = spy(ServiceThread.class);
mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread);
- assertTrue(mDataAggregator.initialize());
// Network logging may log multiple and callbacks, so create a latch to wait for
// the given event.
@@ -578,6 +528,9 @@
}
@Test
+ @RequireRunOnSystemUser
+ @EnsureHasPermission(
+ android.Manifest.permission.BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE)
public void test_StartIntrusionDetectionEventTransportService() {
final String TAG = "test_StartIntrusionDetectionEventTransportService";
ServiceConnection serviceConnection = null;
@@ -639,6 +592,20 @@
return serviceConnection;
}
+ @Test
+ @RequireRunOnSystemUser
+ @EnsureHasPermission(
+ android.Manifest.permission.BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE)
+ public void testIntrusionDetectionEventTransportConnection_isValidAndBinds()
+ throws InterruptedException {
+ IntrusionDetectionEventTransportConnection intrusionDetectionEventTransportConnection =
+ new IntrusionDetectionEventTransportConnection(mContext);
+ // In a real scenario, the connection will be initialized by the service.
+ // Just to show that the connection is valid and able to bind,
+ // we initialize it here.
+ assertTrue(intrusionDetectionEventTransportConnection.initialize());
+ }
+
private class MockInjector implements IntrusionDetectionService.Injector {
private final Context mContext;
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/AndroidManifest.xml b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/AndroidManifest.xml
index 7cc75ab..a1a7e29 100644
--- a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/AndroidManifest.xml
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/AndroidManifest.xml
@@ -16,9 +16,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.coretests.apps.testapp">
+ <uses-permission android:name="android.permission.BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE" />
<application>
<service android:name=".TestLoggingService"
- android:exported="true" />
+ android:exported="true"
+ android:permission="android.permission.BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE" />
</application>
</manifest>
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index 8024915..9850cb0 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -16,7 +16,12 @@
package com.android.server;
-import static com.android.server.GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
+import static android.service.quickaccesswallet.Flags.FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP;
+import static android.service.quickaccesswallet.Flags.launchWalletOptionOnPowerDoubleTap;
+
+import static com.android.server.GestureLauncherService.LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER;
+import static com.android.server.GestureLauncherService.LAUNCH_WALLET_ON_DOUBLE_TAP_POWER;
+import static com.android.server.GestureLauncherService.POWER_DOUBLE_TAP_MAX_TIME_MS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -24,19 +29,27 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
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.app.PendingIntent;
import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.res.Resources;
import android.os.Looper;
import android.os.UserHandle;
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;
+import android.service.quickaccesswallet.QuickAccessWalletClient;
import android.telecom.TelecomManager;
import android.test.mock.MockContentResolver;
import android.testing.TestableLooper;
@@ -55,6 +68,7 @@
import org.junit.Before;
import org.junit.BeforeClass;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -62,6 +76,8 @@
import org.mockito.MockitoAnnotations;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Unit tests for {@link GestureLauncherService}.
@@ -90,9 +106,32 @@
private @Mock TelecomManager mTelecomManager;
private @Mock MetricsLogger mMetricsLogger;
@Mock private UiEventLogger mUiEventLogger;
+ @Mock private QuickAccessWalletClient mQuickAccessWalletClient;
private MockContentResolver mContentResolver;
private GestureLauncherService mGestureLauncherService;
+ private Context mInstrumentationContext =
+ InstrumentationRegistry.getInstrumentation().getContext();
+
+ private static final String LAUNCH_TEST_WALLET_ACTION = "LAUNCH_TEST_WALLET_ACTION";
+ private static final String LAUNCH_FALLBACK_ACTION = "LAUNCH_FALLBACK_ACTION";
+ private PendingIntent mGesturePendingIntent =
+ PendingIntent.getBroadcast(
+ mInstrumentationContext,
+ 0,
+ new Intent(LAUNCH_TEST_WALLET_ACTION),
+ PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);
+
+ private PendingIntent mFallbackPendingIntent =
+ PendingIntent.getBroadcast(
+ mInstrumentationContext,
+ 0,
+ new Intent(LAUNCH_FALLBACK_ACTION),
+ PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@BeforeClass
public static void oneTimeInitialization() {
if (Looper.myLooper() == null) {
@@ -115,9 +154,49 @@
when(mContext.getContentResolver()).thenReturn(mContentResolver);
when(mContext.getSystemService(Context.TELECOM_SERVICE)).thenReturn(mTelecomManager);
when(mTelecomManager.createLaunchEmergencyDialerIntent(null)).thenReturn(new Intent());
+ when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(true);
- mGestureLauncherService = new GestureLauncherService(mContext, mMetricsLogger,
- mUiEventLogger);
+ mGestureLauncherService =
+ new GestureLauncherService(
+ mContext, mMetricsLogger, mQuickAccessWalletClient, mUiEventLogger);
+
+ withDoubleTapPowerGestureEnableSettingValue(true);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+ }
+
+ private WalletLaunchedReceiver registerWalletLaunchedReceiver(String action) {
+ IntentFilter filter = new IntentFilter(action);
+ WalletLaunchedReceiver receiver = new WalletLaunchedReceiver();
+ mInstrumentationContext.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED);
+ return receiver;
+ }
+
+ /**
+ * A simple {@link BroadcastReceiver} implementation that counts down a {@link CountDownLatch}
+ * when a matching message is received
+ */
+ private static final class WalletLaunchedReceiver extends BroadcastReceiver {
+ private static final int TIMEOUT_SECONDS = 3;
+
+ private final CountDownLatch mLatch;
+
+ WalletLaunchedReceiver() {
+ mLatch = new CountDownLatch(1);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mLatch.countDown();
+ context.unregisterReceiver(this);
+ }
+
+ Boolean waitUntilShown() {
+ try {
+ return mLatch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ return false;
+ }
+ }
}
@Test
@@ -134,37 +213,123 @@
@Test
public void testIsCameraDoubleTapPowerSettingEnabled_configFalseSettingDisabled() {
- withCameraDoubleTapPowerEnableConfigValue(false);
- withCameraDoubleTapPowerDisableSettingValue(1);
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ withDoubleTapPowerEnabledConfigValue(false);
+ withDoubleTapPowerGestureEnableSettingValue(false);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+ } else {
+ withCameraDoubleTapPowerEnableConfigValue(false);
+ withCameraDoubleTapPowerDisableSettingValue(1);
+ }
assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
mContext, FAKE_USER_ID));
}
@Test
public void testIsCameraDoubleTapPowerSettingEnabled_configFalseSettingEnabled() {
- withCameraDoubleTapPowerEnableConfigValue(false);
- withCameraDoubleTapPowerDisableSettingValue(0);
- assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
- mContext, FAKE_USER_ID));
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ withDoubleTapPowerEnabledConfigValue(false);
+ withDoubleTapPowerGestureEnableSettingValue(true);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+ assertTrue(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
+ } else {
+ withCameraDoubleTapPowerEnableConfigValue(false);
+ withCameraDoubleTapPowerDisableSettingValue(0);
+ assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
+ }
}
@Test
public void testIsCameraDoubleTapPowerSettingEnabled_configTrueSettingDisabled() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(1);
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ withDoubleTapPowerEnabledConfigValue(true);
+ withDoubleTapPowerGestureEnableSettingValue(false);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+ } else {
+ withCameraDoubleTapPowerEnableConfigValue(true);
+ withCameraDoubleTapPowerDisableSettingValue(1);
+ }
assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
mContext, FAKE_USER_ID));
}
@Test
public void testIsCameraDoubleTapPowerSettingEnabled_configTrueSettingEnabled() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ withDoubleTapPowerEnabledConfigValue(true);
+ withDoubleTapPowerGestureEnableSettingValue(true);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+ } else {
+ withCameraDoubleTapPowerEnableConfigValue(true);
+ withCameraDoubleTapPowerDisableSettingValue(0);
+ }
assertTrue(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
mContext, FAKE_USER_ID));
}
@Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testIsCameraDoubleTapPowerSettingEnabled_actionWallet() {
+ withDoubleTapPowerEnabledConfigValue(true);
+ withDoubleTapPowerGestureEnableSettingValue(true);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
+
+ assertFalse(
+ mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testIsWalletDoubleTapPowerSettingEnabled() {
+ withDoubleTapPowerEnabledConfigValue(true);
+ withDoubleTapPowerGestureEnableSettingValue(true);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
+
+ assertTrue(
+ mGestureLauncherService.isWalletDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testIsWalletDoubleTapPowerSettingEnabled_configDisabled() {
+ withDoubleTapPowerEnabledConfigValue(false);
+ withDoubleTapPowerGestureEnableSettingValue(true);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
+
+ assertTrue(
+ mGestureLauncherService.isWalletDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testIsWalletDoubleTapPowerSettingEnabled_settingDisabled() {
+ withDoubleTapPowerEnabledConfigValue(true);
+ withDoubleTapPowerGestureEnableSettingValue(false);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
+
+ assertFalse(
+ mGestureLauncherService.isWalletDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testIsWalletDoubleTapPowerSettingEnabled_actionCamera() {
+ withDoubleTapPowerEnabledConfigValue(true);
+ withDoubleTapPowerGestureEnableSettingValue(true);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+
+ assertFalse(
+ mGestureLauncherService.isWalletDoubleTapPowerSettingEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
public void testIsEmergencyGestureSettingEnabled_settingDisabled() {
withEmergencyGestureEnabledConfigValue(true);
withEmergencyGestureEnabledSettingValue(false);
@@ -245,12 +410,9 @@
@Test
public void testInterceptPowerKeyDown_firstPowerDownCameraPowerGestureOnInteractive() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ enableCameraGesture();
- long eventTime = INITIAL_EVENT_TIME_MILLIS +
- CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ long eventTime = INITIAL_EVENT_TIME_MILLIS + POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
boolean interactive = true;
@@ -284,8 +446,12 @@
@Test
public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOffInteractive() {
- withCameraDoubleTapPowerEnableConfigValue(false);
- withCameraDoubleTapPowerDisableSettingValue(1);
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ withDoubleTapPowerGestureEnableSettingValue(false);
+ } else {
+ withCameraDoubleTapPowerEnableConfigValue(false);
+ withCameraDoubleTapPowerDisableSettingValue(1);
+ }
mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
@@ -298,7 +464,7 @@
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -329,8 +495,12 @@
@Test
public void testInterceptPowerKeyDown_intervalMidBoundsCameraPowerGestureOffInteractive() {
- withCameraDoubleTapPowerEnableConfigValue(false);
- withCameraDoubleTapPowerDisableSettingValue(1);
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ withDoubleTapPowerGestureEnableSettingValue(false);
+ } else {
+ withCameraDoubleTapPowerEnableConfigValue(false);
+ withCameraDoubleTapPowerDisableSettingValue(1);
+ }
mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
@@ -343,7 +513,7 @@
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -422,10 +592,7 @@
@Test
public void
testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnInteractiveSetupComplete() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
- withUserSetupCompleteValue(true);
+ enableCameraGesture();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -437,7 +604,7 @@
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -470,15 +637,145 @@
}
@Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void
+ testInterceptPowerKeyDown_fiveInboundPresses_walletAndEmergencyEnabled_bothLaunch() {
+ WalletLaunchedReceiver receiver = registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION);
+ setUpGetGestureTargetActivityPendingIntent(mGesturePendingIntent);
+ enableEmergencyGesture();
+ enableWalletGesture();
+
+ // First event
+ long eventTime = INITIAL_EVENT_TIME_MILLIS;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ eventTime += interval;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, true);
+
+ assertTrue(receiver.waitUntilShown());
+
+ // Presses 3 and 4 should not trigger any gesture
+ for (int i = 0; i < 2; i++) {
+ eventTime += interval;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, false);
+ }
+
+ // Fifth button press should trigger the emergency flow
+ eventTime += interval;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, true);
+
+ verify(mUiEventLogger, times(1))
+ .log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER);
+ verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testInterceptPowerKeyDown_intervalInBoundsWalletPowerGesture() {
+ WalletLaunchedReceiver receiver = registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION);
+ setUpGetGestureTargetActivityPendingIntent(mGesturePendingIntent);
+ enableWalletGesture();
+ enableEmergencyGesture();
+
+ long eventTime = INITIAL_EVENT_TIME_MILLIS;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ eventTime += interval;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, true);
+ assertTrue(receiver.waitUntilShown());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testInterceptPowerKeyDown_walletGestureOn_quickAccessWalletServiceUnavailable() {
+ when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false);
+ WalletLaunchedReceiver receiver = registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION);
+ setUpGetGestureTargetActivityPendingIntent(mGesturePendingIntent);
+ enableWalletGesture();
+
+ // First event
+ long eventTime = INITIAL_EVENT_TIME_MILLIS;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ eventTime += interval;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, false);
+
+ assertFalse(receiver.waitUntilShown());
+ }
+
+ @Test
+ public void testInterceptPowerKeyDown_walletGestureOn_userSetupIncomplete() {
+ WalletLaunchedReceiver receiver = registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION);
+ setUpGetGestureTargetActivityPendingIntent(mGesturePendingIntent);
+ enableWalletGesture();
+ withUserSetupCompleteValue(false);
+
+ // First event
+ long eventTime = INITIAL_EVENT_TIME_MILLIS;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ eventTime += interval;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+ assertFalse(receiver.waitUntilShown());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testInterceptPowerKeyDown_walletPowerGesture_nullPendingIntent() {
+ WalletLaunchedReceiver gestureReceiver =
+ registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION);
+ setUpGetGestureTargetActivityPendingIntent(null);
+ WalletLaunchedReceiver fallbackReceiver =
+ registerWalletLaunchedReceiver(LAUNCH_FALLBACK_ACTION);
+ setUpWalletFallbackPendingIntent(mFallbackPendingIntent);
+ enableWalletGesture();
+ enableEmergencyGesture();
+
+ // First event
+ long eventTime = INITIAL_EVENT_TIME_MILLIS;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ eventTime += interval;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, true);
+
+ assertFalse(gestureReceiver.waitUntilShown());
+ assertTrue(fallbackReceiver.waitUntilShown());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+ public void testInterceptPowerKeyDown_walletPowerGesture_intervalOutOfBounds() {
+ WalletLaunchedReceiver gestureReceiver =
+ registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION);
+ setUpGetGestureTargetActivityPendingIntent(null);
+ WalletLaunchedReceiver fallbackReceiver =
+ registerWalletLaunchedReceiver(LAUNCH_FALLBACK_ACTION);
+ setUpWalletFallbackPendingIntent(mFallbackPendingIntent);
+ enableWalletGesture();
+ enableEmergencyGesture();
+
+ // First event
+ long eventTime = INITIAL_EVENT_TIME_MILLIS;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS;
+ eventTime += interval;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+ assertFalse(gestureReceiver.waitUntilShown());
+ assertFalse(fallbackReceiver.waitUntilShown());
+ }
+
+ @Test
public void
testInterceptPowerKeyDown_fiveInboundPresses_cameraAndEmergencyEnabled_bothLaunch() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- withEmergencyGestureEnabledConfigValue(true);
- withEmergencyGestureEnabledSettingValue(true);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
- mGestureLauncherService.updateEmergencyGestureEnabled();
- withUserSetupCompleteValue(true);
+ enableCameraGesture();
+ enableEmergencyGesture();
// First button press does nothing
long eventTime = INITIAL_EVENT_TIME_MILLIS;
@@ -491,7 +788,7 @@
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
// 2nd button triggers camera
eventTime += interval;
@@ -580,7 +877,7 @@
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
// 3 more button presses which should not trigger any gesture (camera gesture disabled)
for (int i = 0; i < 3; i++) {
eventTime += interval;
@@ -634,7 +931,7 @@
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
// 3 more button presses which should not trigger any gesture, but intercepts action.
for (int i = 0; i < 3; i++) {
eventTime += interval;
@@ -737,7 +1034,7 @@
interactive, outLaunched);
assertTrue(intercepted);
assertFalse(outLaunched.value);
- interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
}
}
@@ -765,7 +1062,7 @@
interactive, outLaunched);
assertTrue(intercepted);
assertFalse(outLaunched.value);
- interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
}
}
@@ -916,7 +1213,7 @@
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT, IGNORED_META_STATE, IGNORED_DEVICE_ID, IGNORED_SCANCODE,
@@ -947,9 +1244,7 @@
@Test
public void
testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnInteractiveSetupIncomplete() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ enableCameraGesture();
withUserSetupCompleteValue(false);
long eventTime = INITIAL_EVENT_TIME_MILLIS;
@@ -962,7 +1257,7 @@
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -995,9 +1290,7 @@
@Test
public void testInterceptPowerKeyDown_intervalMidBoundsCameraPowerGestureOnInteractive() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ enableCameraGesture();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1009,7 +1302,7 @@
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -1042,9 +1335,7 @@
@Test
public void testInterceptPowerKeyDown_intervalOutOfBoundsCameraPowerGestureOnInteractive() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ enableCameraGesture();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1087,8 +1378,12 @@
@Test
public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOffNotInteractive() {
- withCameraDoubleTapPowerEnableConfigValue(false);
- withCameraDoubleTapPowerDisableSettingValue(1);
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ withDoubleTapPowerGestureEnableSettingValue(false);
+ } else {
+ withCameraDoubleTapPowerEnableConfigValue(false);
+ withCameraDoubleTapPowerDisableSettingValue(1);
+ }
mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
@@ -1101,7 +1396,7 @@
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -1146,7 +1441,7 @@
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -1223,10 +1518,7 @@
@Test
public void
testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnNotInteractiveSetupComplete() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
- withUserSetupCompleteValue(true);
+ enableCameraGesture();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1238,7 +1530,7 @@
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -1272,9 +1564,7 @@
@Test
public void
testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnNotInteractiveSetupIncomplete() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ enableCameraGesture();
withUserSetupCompleteValue(false);
long eventTime = INITIAL_EVENT_TIME_MILLIS;
@@ -1287,7 +1577,7 @@
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -1332,7 +1622,7 @@
assertFalse(intercepted);
assertFalse(outLaunched.value);
- final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS;
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -1365,9 +1655,7 @@
@Test
public void testInterceptPowerKeyDown_intervalOutOfBoundsCameraPowerGestureOnNotInteractive() {
- withCameraDoubleTapPowerEnableConfigValue(true);
- withCameraDoubleTapPowerDisableSettingValue(0);
- mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ enableCameraGesture();
long eventTime = INITIAL_EVENT_TIME_MILLIS;
KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1409,12 +1697,53 @@
}
/**
+ * If processPowerKeyDown is called instead of interceptPowerKeyDown (meaning the double tap
+ * gesture isn't performed), the emergency gesture is still launched.
+ */
+ @Test
+ public void
+ testProcessPowerKeyDown_fiveInboundPresses_cameraDoesNotLaunch_emergencyGestureLaunches() {
+ enableCameraGesture();
+ enableEmergencyGesture();
+
+ // First event
+ long eventTime = INITIAL_EVENT_TIME_MILLIS;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+ //Second event; call processPowerKeyDown without calling interceptPowerKeyDown
+ final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ eventTime += interval;
+ KeyEvent keyEvent =
+ new KeyEvent(
+ IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT);
+ mGestureLauncherService.processPowerKeyDown(keyEvent);
+
+ verify(mMetricsLogger, never())
+ .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+ verify(mUiEventLogger, never()).log(any());
+
+ // Presses 3 and 4 should not trigger any gesture
+ for (int i = 0; i < 2; i++) {
+ eventTime += interval;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, false);
+ }
+
+ // Fifth button press should still trigger the emergency flow
+ eventTime += interval;
+ sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, true);
+
+ verify(mUiEventLogger, times(1))
+ .log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER);
+ verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected();
+ }
+
+ /**
* Helper method to trigger emergency gesture by pressing button for 5 times.
*
* @return last event time.
*/
private long triggerEmergencyGesture() {
- return triggerEmergencyGesture(CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1);
+ return triggerEmergencyGesture(POWER_DOUBLE_TAP_MAX_TIME_MS - 1);
}
/**
@@ -1473,6 +1802,27 @@
.thenReturn(enableConfigValue);
}
+ private void withDoubleTapPowerEnabledConfigValue(boolean enable) {
+ when(mResources.getBoolean(com.android.internal.R.bool.config_doubleTapPowerGestureEnabled))
+ .thenReturn(enable);
+ }
+
+ private void withDoubleTapPowerGestureEnableSettingValue(boolean enable) {
+ Settings.Secure.putIntForUser(
+ mContentResolver,
+ Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED,
+ enable ? 1 : 0,
+ UserHandle.USER_CURRENT);
+ }
+
+ private void withDefaultDoubleTapPowerGestureAction(int action) {
+ Settings.Secure.putIntForUser(
+ mContentResolver,
+ Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE,
+ action,
+ UserHandle.USER_CURRENT);
+ }
+
private void withEmergencyGestureEnabledConfigValue(boolean enableConfigValue) {
when(mResources.getBoolean(
com.android.internal.R.bool.config_emergencyGestureEnabled))
@@ -1510,4 +1860,72 @@
userSetupCompleteValue,
UserHandle.USER_CURRENT);
}
+
+ private void setUpGetGestureTargetActivityPendingIntent(PendingIntent pendingIntent) {
+ doAnswer(
+ invocation -> {
+ QuickAccessWalletClient.GesturePendingIntentCallback callback =
+ (QuickAccessWalletClient.GesturePendingIntentCallback)
+ invocation.getArguments()[1];
+ callback.onGesturePendingIntentRetrieved(pendingIntent);
+ return null;
+ })
+ .when(mQuickAccessWalletClient)
+ .getGestureTargetActivityPendingIntent(any(), any());
+ }
+
+ private void setUpWalletFallbackPendingIntent(PendingIntent pendingIntent) {
+ doAnswer(
+ invocation -> {
+ QuickAccessWalletClient.WalletPendingIntentCallback callback =
+ (QuickAccessWalletClient.WalletPendingIntentCallback)
+ invocation.getArguments()[1];
+ callback.onWalletPendingIntentRetrieved(pendingIntent);
+ return null;
+ })
+ .when(mQuickAccessWalletClient)
+ .getWalletPendingIntent(any(), any());
+ }
+
+ private void enableWalletGesture() {
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
+ withDoubleTapPowerGestureEnableSettingValue(true);
+ withDoubleTapPowerEnabledConfigValue(true);
+
+ mGestureLauncherService.updateWalletDoubleTapPowerEnabled();
+ withUserSetupCompleteValue(true);
+ }
+
+ private void enableEmergencyGesture() {
+ withEmergencyGestureEnabledConfigValue(true);
+ withEmergencyGestureEnabledSettingValue(true);
+ mGestureLauncherService.updateEmergencyGestureEnabled();
+ withUserSetupCompleteValue(true);
+ }
+
+ private void enableCameraGesture() {
+ if (launchWalletOptionOnPowerDoubleTap()) {
+ withDoubleTapPowerEnabledConfigValue(true);
+ withDoubleTapPowerGestureEnableSettingValue(true);
+ withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+ } else {
+ withCameraDoubleTapPowerEnableConfigValue(true);
+ withCameraDoubleTapPowerDisableSettingValue(0);
+ }
+ mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ withUserSetupCompleteValue(true);
+ }
+
+ private void sendPowerKeyDownToGestureLauncherServiceAndAssertValues(
+ long eventTime, boolean expectedIntercept, boolean expectedOutLaunchedValue) {
+ KeyEvent keyEvent =
+ new KeyEvent(
+ IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT);
+ boolean interactive = true;
+ MutableBoolean outLaunched = new MutableBoolean(true);
+ boolean intercepted =
+ mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, outLaunched);
+ assertEquals(intercepted, expectedIntercept);
+ assertEquals(outLaunched.value, expectedOutLaunchedValue);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 5127b2d..c6df146 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -22,6 +22,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
@@ -921,6 +923,41 @@
});
}
+ @Test
+ public void shouldShowRdmEduDialog1() {
+ // RDM V1 Cases
+ assertTrue(DeviceStateManagerService.shouldShowRdmEduDialog(
+ false /* hasControlDeviceStatePermission */,
+ false /* requestingRdmOuterDefault */,
+ false /* isDeviceClosed (no-op) */));
+
+ assertFalse(DeviceStateManagerService.shouldShowRdmEduDialog(
+ true /* hasControlDeviceStatePermission */,
+ false /* requestingRdmOuterDefault */,
+ true /* isDeviceClosed (no-op) */));
+
+ // RDM V2 Cases
+ // hasControlDeviceStatePermission = false
+ assertFalse(DeviceStateManagerService.shouldShowRdmEduDialog(
+ false /* hasControlDeviceStatePermission */,
+ true /* requestingRdmOuterDefault */,
+ false /* isDeviceClosed */));
+ assertTrue(DeviceStateManagerService.shouldShowRdmEduDialog(
+ false /* hasControlDeviceStatePermission */,
+ true /* requestingRdmOuterDefault */,
+ true /* isDeviceClosed */));
+
+ // hasControlDeviceStatePermission = true
+ assertFalse(DeviceStateManagerService.shouldShowRdmEduDialog(
+ true /* hasControlDeviceStatePermission */,
+ true /* requestingRdmOuterDefault */,
+ false /* isDeviceClosed */));
+ assertFalse(DeviceStateManagerService.shouldShowRdmEduDialog(
+ true /* hasControlDeviceStatePermission */,
+ true /* requestingRdmOuterDefault */,
+ true /* isDeviceClosed */));
+ }
+
/**
* Common code to verify the handling of FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP flag.
*
diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
index 27486b7..71a05f3 100644
--- a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
@@ -286,6 +286,25 @@
}
@Test
+ public void testPredictTargets_emptyIntentFilter() {
+ Bundle bundle = new Bundle();
+ IntentFilter filter = new IntentFilter();
+ bundle.putObject(ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY, filter);
+ AppPredictionContext predictionContext = new AppPredictionContext.Builder(mContext)
+ .setUiSurface(UI_SURFACE_SHARE)
+ .setPredictedTargetCount(NUM_PREDICTED_TARGETS)
+ .setExtras(bundle)
+ .build();
+ mPredictor = new ShareTargetPredictor(
+ predictionContext, mUpdatePredictionsMethod, mDataManager, USER_ID, mContext);
+
+ mPredictor.predictTargets();
+
+ verify(mUpdatePredictionsMethod).accept(mAppTargetCaptor.capture());
+ assertThat(mAppTargetCaptor.getValue()).isEmpty();
+ }
+
+ @Test
public void testPredictTargets_noSharingHistoryRankedByShortcutRank() {
mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1", 3));
mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2", 2));
diff --git a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
index a8544f6..5862ac6 100644
--- a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
+++ b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
@@ -26,6 +26,7 @@
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
+import android.content.pm.PackageManager
import android.content.pm.UserInfo
import android.os.Handler
import android.os.PersistableBundle
@@ -59,6 +60,7 @@
@get:Rule val mocks: MockitoRule = MockitoJUnit.rule()
@Mock private lateinit var mockDpmInternal: DevicePolicyManagerInternal
+ @Mock private lateinit var mockPackageManager: PackageManager
@Mock private lateinit var mockUserManagerInternal: UserManagerInternal
private lateinit var context: Context
@@ -68,7 +70,7 @@
@Before
fun setUp() {
context = InstrumentationRegistry.getInstrumentation().context
- context = SupervisionContextWrapper(context)
+ context = SupervisionContextWrapper(context, mockPackageManager)
LocalServices.removeServiceForTest(DevicePolicyManagerInternal::class.java)
LocalServices.addService(DevicePolicyManagerInternal::class.java, mockDpmInternal)
@@ -137,6 +139,31 @@
}
@Test
+ fun isActiveSupervisionApp_supervisionUid_supervisionEnabled_returnsTrue() {
+ whenever(mockPackageManager.getPackagesForUid(APP_UID))
+ .thenReturn(arrayOf(systemSupervisionPackage))
+ service.setSupervisionEnabledForUser(USER_ID, true)
+
+ assertThat(service.mInternal.isActiveSupervisionApp(APP_UID)).isTrue()
+ }
+
+ @Test
+ fun isActiveSupervisionApp_supervisionUid_supervisionNotEnabled_returnsFalse() {
+ whenever(mockPackageManager.getPackagesForUid(APP_UID))
+ .thenReturn(arrayOf(systemSupervisionPackage))
+ service.setSupervisionEnabledForUser(USER_ID, false)
+
+ assertThat(service.mInternal.isActiveSupervisionApp(APP_UID)).isFalse()
+ }
+
+ @Test
+ fun isActiveSupervisionApp_notSupervisionUid_returnsFalse() {
+ whenever(mockPackageManager.getPackagesForUid(APP_UID)).thenReturn(arrayOf())
+
+ assertThat(service.mInternal.isActiveSupervisionApp(APP_UID)).isFalse()
+ }
+
+ @Test
fun setSupervisionEnabledForUser() {
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
@@ -191,6 +218,7 @@
private companion object {
const val USER_ID = 100
+ val APP_UID = USER_ID * UserHandle.PER_USER_RANGE
}
}
@@ -198,9 +226,12 @@
* A context wrapper that allows broadcast intents to immediately invoke the receivers without
* performing checks on the sending user.
*/
-private class SupervisionContextWrapper(val context: Context) : ContextWrapper(context) {
+private class SupervisionContextWrapper(val context: Context, val pkgManager: PackageManager) :
+ ContextWrapper(context) {
val interceptors = mutableListOf<Pair<BroadcastReceiver, IntentFilter>>()
+ override fun getPackageManager() = pkgManager
+
override fun registerReceiverForAllUsers(
receiver: BroadcastReceiver?,
filter: IntentFilter,
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index 0404b82..c4b8599 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -118,6 +118,7 @@
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
@@ -232,7 +233,8 @@
any(AlarmManager.OnAlarmListener.class), any(Handler.class));
doAnswer(inv -> {
- mCustomListener = () -> {};
+ mCustomListener = () -> {
+ };
return null;
}).when(mAlarmManager).cancel(eq(mCustomListener));
when(mContext.getSystemService(eq(Context.POWER_SERVICE)))
@@ -1321,7 +1323,7 @@
@Test
public void enableCarMode_failsForBogusPackageName() throws Exception {
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.DEFAULT_CALLING_UID + 1);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID + 1);
assertThrows(SecurityException.class, () -> mService.enableCarMode(0, 0, PACKAGE_NAME));
assertThat(mService.getCurrentModeType()).isNotEqualTo(Configuration.UI_MODE_TYPE_CAR);
@@ -1343,19 +1345,19 @@
@Test
public void disableCarMode_failsForBogusPackageName() throws Exception {
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.DEFAULT_CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
mService.enableCarMode(0, 0, PACKAGE_NAME);
assertThat(mService.getCurrentModeType()).isEqualTo(Configuration.UI_MODE_TYPE_CAR);
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.DEFAULT_CALLING_UID + 1);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID + 1);
assertThrows(SecurityException.class,
- () -> mService.disableCarModeByCallingPackage(0, PACKAGE_NAME));
+ () -> mService.disableCarModeByCallingPackage(0, PACKAGE_NAME));
assertThat(mService.getCurrentModeType()).isEqualTo(Configuration.UI_MODE_TYPE_CAR);
// Clean up
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.DEFAULT_CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
mService.disableCarModeByCallingPackage(0, PACKAGE_NAME);
assertThat(mService.getCurrentModeType()).isNotEqualTo(Configuration.UI_MODE_TYPE_CAR);
}
@@ -1473,12 +1475,13 @@
assertFalse(mUiManagerService.getConfiguration().isNightModeActive());
}
- // attention modes with expected night modes
- Map<Integer, Boolean> modes = Map.of(
- MODE_ATTENTION_THEME_OVERLAY_OFF, initialNightMode,
- MODE_ATTENTION_THEME_OVERLAY_DAY, false,
- MODE_ATTENTION_THEME_OVERLAY_NIGHT, true
- );
+ // Attention modes with expected night modes.
+ // Important to keep modes.put(MODE_ATTENTION_THEME_OVERLAY_OFF, initialNightMode) in the
+ // first position, hence LinkedHashMap.
+ Map<Integer, Boolean> modes = new LinkedHashMap<>();
+ modes.put(MODE_ATTENTION_THEME_OVERLAY_OFF, initialNightMode);
+ modes.put(MODE_ATTENTION_THEME_OVERLAY_DAY, false);
+ modes.put(MODE_ATTENTION_THEME_OVERLAY_NIGHT, true);
// test
for (int attentionMode : modes.keySet()) {
@@ -1562,7 +1565,7 @@
private final int callingUid;
public TestInjector() {
- this(DEFAULT_CALLING_UID);
+ this(DEFAULT_CALLING_UID);
}
public TestInjector(int callingUid) {
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 90bf1d3..074cbb5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -49,10 +49,14 @@
import static android.app.NotificationChannel.RECS_ID;
import static android.app.NotificationChannel.SOCIAL_MEDIA_ID;
import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
+import static android.app.NotificationManager.ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED;
import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
+import static android.app.NotificationManager.EXTRA_AUTOMATIC_ZEN_RULE_ID;
+import static android.app.NotificationManager.EXTRA_AUTOMATIC_ZEN_RULE_STATUS;
import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
@@ -369,6 +373,7 @@
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
@@ -11273,19 +11278,71 @@
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)), eq(UserHandle.of(102)));
}
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void onAutomaticRuleStatusChanged_sendsBroadcastToRuleOwner() throws Exception {
+ mService.mZenModeHelper.getCallbacks().forEach(c -> c.onAutomaticRuleStatusChanged(
+ mUserId, "rule.owner.pkg", "rule_id", AUTOMATIC_RULE_STATUS_ACTIVATED));
+
+ Intent expected = new Intent(ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED)
+ .setPackage("rule.owner.pkg")
+ .putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, "rule_id")
+ .putExtra(EXTRA_AUTOMATIC_ZEN_RULE_STATUS, AUTOMATIC_RULE_STATUS_ACTIVATED)
+ .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+
+ verify(mContext).sendBroadcastAsUser(eqIntent(expected), eq(UserHandle.of(mUserId)));
+ }
+
private static Intent eqIntent(Intent wanted) {
return ArgumentMatchers.argThat(
new ArgumentMatcher<Intent>() {
@Override
public boolean matches(Intent argument) {
return wanted.filterEquals(argument)
- && wanted.getFlags() == argument.getFlags();
+ && wanted.getFlags() == argument.getFlags()
+ && equalBundles(wanted.getExtras(), argument.getExtras());
}
@Override
public String toString() {
return wanted.toString();
}
+
+ private boolean equalBundles(Bundle one, Bundle two) {
+ if (one == null && two == null) {
+ return true;
+ }
+ if ((one == null) != (two == null)) {
+ return false;
+ }
+ if (one.size() != two.size()) {
+ return false;
+ }
+
+ HashSet<String> setOne = new HashSet<>(one.keySet());
+ setOne.addAll(two.keySet());
+
+ for (String key : setOne) {
+ if (!one.containsKey(key) || !two.containsKey(key)) {
+ return false;
+ }
+
+ Object valueOne = one.get(key);
+ Object valueTwo = two.get(key);
+ if (valueOne instanceof Bundle
+ && valueTwo instanceof Bundle
+ && !equalBundles((Bundle) valueOne, (Bundle) valueTwo)) {
+ return false;
+ } else if (valueOne == null) {
+ if (valueTwo != null) {
+ return false;
+ }
+ } else if (!valueOne.equals(valueTwo)) {
+ return false;
+ }
+ }
+ return true;
+ }
});
}
@@ -17314,6 +17371,7 @@
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
.build();
@@ -17327,6 +17385,7 @@
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
.build();
StatusBarNotification sbn1 = new StatusBarNotification(mPkg, mPkg, 7, null, mUid, 0,
@@ -17339,6 +17398,7 @@
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
.build();
StatusBarNotification sbn2 = new StatusBarNotification(PKG_O, PKG_O, 7, null, UID_O, 0,
@@ -17388,6 +17448,7 @@
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
.build();
@@ -17420,6 +17481,7 @@
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.setFlag(FLAG_PROMOTED_ONGOING, true) // add manually since we're skipping post
.setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
.build();
@@ -17434,6 +17496,7 @@
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.setFlag(FLAG_PROMOTED_ONGOING, true) // add manually since we're skipping post
.setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
.build();
@@ -17483,6 +17546,7 @@
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.setFlag(FLAG_PROMOTED_ONGOING, true) // add manually since we're skipping post
.setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
.build();
@@ -17515,6 +17579,7 @@
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.build();
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 9, null, mUid, 0,
n, UserHandle.getUserHandleForUid(mUid), null, 0);
@@ -17543,6 +17608,7 @@
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.build();
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 9, null, mUid, 0,
@@ -17570,6 +17636,7 @@
.setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
.setColor(Color.WHITE)
.setColorized(true)
+ .setOngoing(true)
.build();
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 9, null, mUid, 0,
diff --git a/services/tests/wmtests/res/xml/bookmarks.xml b/services/tests/wmtests/res/xml/bookmarks.xml
index 787f4e8..3fc7c76 100644
--- a/services/tests/wmtests/res/xml/bookmarks.xml
+++ b/services/tests/wmtests/res/xml/bookmarks.xml
@@ -22,7 +22,7 @@
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CONTACTS"
- androidprv:keycode="KEYCODE_C"
+ androidprv:keycode="KEYCODE_P"
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_EMAIL"
@@ -30,21 +30,13 @@
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CALENDAR"
- androidprv:keycode="KEYCODE_K"
+ androidprv:keycode="KEYCODE_C"
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_MAPS"
androidprv:keycode="KEYCODE_M"
androidprv:modifierState="META" />
<bookmark
- category="android.intent.category.APP_MUSIC"
- androidprv:keycode="KEYCODE_P"
- androidprv:modifierState="META" />
- <bookmark
- role="android.app.role.SMS"
- androidprv:keycode="KEYCODE_S"
- androidprv:modifierState="META" />
- <bookmark
category="android.intent.category.APP_CALCULATOR"
androidprv:keycode="KEYCODE_U"
androidprv:modifierState="META" />
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
index 1e9038e..41865b20 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
@@ -258,9 +258,9 @@
{"EXPLORER key -> Launch Default Browser", new int[]{KeyEvent.KEYCODE_EXPLORER},
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER,
KeyEvent.KEYCODE_EXPLORER, 0},
- {"Meta + C -> Launch Default Contacts", new int[]{META_KEY, KeyEvent.KEYCODE_C},
+ {"Meta + P -> Launch Default Contacts", new int[]{META_KEY, KeyEvent.KEYCODE_P},
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS,
- KeyEvent.KEYCODE_C, META_ON},
+ KeyEvent.KEYCODE_P, META_ON},
{"CONTACTS key -> Launch Default Contacts", new int[]{KeyEvent.KEYCODE_CONTACTS},
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS,
KeyEvent.KEYCODE_CONTACTS, 0},
@@ -270,15 +270,12 @@
{"ENVELOPE key -> Launch Default Email", new int[]{KeyEvent.KEYCODE_ENVELOPE},
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL,
KeyEvent.KEYCODE_ENVELOPE, 0},
- {"Meta + K -> Launch Default Calendar", new int[]{META_KEY, KeyEvent.KEYCODE_K},
+ {"Meta + C -> Launch Default Calendar", new int[]{META_KEY, KeyEvent.KEYCODE_C},
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR,
- KeyEvent.KEYCODE_K, META_ON},
+ KeyEvent.KEYCODE_C, META_ON},
{"CALENDAR key -> Launch Default Calendar", new int[]{KeyEvent.KEYCODE_CALENDAR},
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR,
KeyEvent.KEYCODE_CALENDAR, 0},
- {"Meta + P -> Launch Default Music", new int[]{META_KEY, KeyEvent.KEYCODE_P},
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC,
- KeyEvent.KEYCODE_P, META_ON},
{"MUSIC key -> Launch Default Music", new int[]{KeyEvent.KEYCODE_MUSIC},
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC,
KeyEvent.KEYCODE_MUSIC, 0},
@@ -291,11 +288,7 @@
KeyEvent.KEYCODE_CALCULATOR, 0},
{"Meta + M -> Launch Default Maps", new int[]{META_KEY, KeyEvent.KEYCODE_M},
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS,
- KeyEvent.KEYCODE_M, META_ON},
- {"Meta + S -> Launch Default Messaging App",
- new int[]{META_KEY, KeyEvent.KEYCODE_S},
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING,
- KeyEvent.KEYCODE_S, META_ON}};
+ KeyEvent.KEYCODE_M, META_ON}};
}
@Keep
@@ -468,6 +461,14 @@
}
@Test
+ public void testKeyGestureLaunchVoiceAssistant() {
+ Assert.assertTrue(
+ sendKeyGestureEventComplete(
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT));
+ mPhoneWindowManager.assertSearchManagerLaunchAssist();
+ }
+
+ @Test
public void testKeyGestureGoHome() {
Assert.assertTrue(
sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_HOME));
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
index cf5323e..35b077e 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
@@ -26,7 +26,6 @@
import static android.view.KeyEvent.KEYCODE_ENTER;
import static android.view.KeyEvent.KEYCODE_H;
import static android.view.KeyEvent.KEYCODE_J;
-import static android.view.KeyEvent.KEYCODE_K;
import static android.view.KeyEvent.KEYCODE_M;
import static android.view.KeyEvent.KEYCODE_META_LEFT;
import static android.view.KeyEvent.KEYCODE_N;
@@ -67,14 +66,12 @@
// These shortcuts should align with those defined in
// services/tests/wmtests/res/xml/bookmarks.xml
INTENT_SHORTCUTS.append(KEYCODE_U, Intent.CATEGORY_APP_CALCULATOR);
- INTENT_SHORTCUTS.append(KEYCODE_C, Intent.CATEGORY_APP_CONTACTS);
+ INTENT_SHORTCUTS.append(KEYCODE_P, Intent.CATEGORY_APP_CONTACTS);
INTENT_SHORTCUTS.append(KEYCODE_E, Intent.CATEGORY_APP_EMAIL);
- INTENT_SHORTCUTS.append(KEYCODE_K, Intent.CATEGORY_APP_CALENDAR);
+ INTENT_SHORTCUTS.append(KEYCODE_C, Intent.CATEGORY_APP_CALENDAR);
INTENT_SHORTCUTS.append(KEYCODE_M, Intent.CATEGORY_APP_MAPS);
- INTENT_SHORTCUTS.append(KEYCODE_P, Intent.CATEGORY_APP_MUSIC);
ROLE_SHORTCUTS.append(KEYCODE_B, RoleManager.ROLE_BROWSER);
- ROLE_SHORTCUTS.append(KEYCODE_S, RoleManager.ROLE_SMS);
}
private static final int ANY_DISPLAY_ID = 123;
@@ -109,7 +106,7 @@
sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_B}, 0);
mPhoneWindowManager.assertLaunchRole(RoleManager.ROLE_BROWSER);
- sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_C}, 0);
+ sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_P}, 0);
mPhoneWindowManager.assertLaunchCategory(Intent.CATEGORY_APP_CONTACTS);
sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_J}, 0);
diff --git a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
index 05a1482..1cc4db9 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
@@ -18,15 +18,22 @@
import static android.view.KeyEvent.KEYCODE_POWER;
import static android.view.KeyEvent.KEYCODE_VOLUME_UP;
+import static com.android.hardware.input.Flags.FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_ASSISTANT;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_GLOBAL_ACTIONS;
+import static com.android.server.policy.PhoneWindowManager.POWER_MULTI_PRESS_TIMEOUT_MILLIS;
import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_POWER_DREAM_OR_SLEEP;
import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_POWER_GO_TO_SLEEP;
+import static org.junit.Assert.assertEquals;
+
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.view.Display;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
/**
@@ -39,8 +46,12 @@
@Before
public void setUp() {
setUpPhoneWindowManager();
+ mPhoneWindowManager.overrideStatusBarManagerInternal();
}
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
/**
* Power single press to turn screen on/off.
*/
@@ -50,6 +61,8 @@
sendKey(KEYCODE_POWER);
mPhoneWindowManager.assertPowerSleep();
+ mPhoneWindowManager.moveTimeForward(POWER_MULTI_PRESS_TIMEOUT_MILLIS);
+
// turn screen on when begin from non-interactive.
mPhoneWindowManager.overrideDisplayState(Display.STATE_OFF);
sendKey(KEYCODE_POWER);
@@ -90,7 +103,7 @@
mPhoneWindowManager.overrideCanStartDreaming(false);
sendKey(KEYCODE_POWER);
sendKey(KEYCODE_POWER);
- mPhoneWindowManager.assertCameraLaunch();
+ mPhoneWindowManager.assertDoublePowerLaunch();
mPhoneWindowManager.assertDidNotLockAfterAppTransitionFinished();
}
@@ -101,7 +114,7 @@
public void testPowerDoublePress() {
sendKey(KEYCODE_POWER);
sendKey(KEYCODE_POWER);
- mPhoneWindowManager.assertCameraLaunch();
+ mPhoneWindowManager.assertDoublePowerLaunch();
}
/**
@@ -111,12 +124,14 @@
public void testPowerLongPress() {
// Show assistant.
mPhoneWindowManager.overrideLongPressOnPower(LONG_PRESS_POWER_ASSISTANT);
- sendKey(KEYCODE_POWER, true);
+ sendKey(KEYCODE_POWER, SingleKeyGestureDetector.sDefaultLongPressTimeout);
mPhoneWindowManager.assertSearchManagerLaunchAssist();
+ mPhoneWindowManager.moveTimeForward(POWER_MULTI_PRESS_TIMEOUT_MILLIS);
+
// Show global actions.
mPhoneWindowManager.overrideLongPressOnPower(LONG_PRESS_POWER_GLOBAL_ACTIONS);
- sendKey(KEYCODE_POWER, true);
+ sendKey(KEYCODE_POWER, SingleKeyGestureDetector.sDefaultLongPressTimeout);
mPhoneWindowManager.assertShowGlobalActionsCalled();
}
@@ -141,4 +156,139 @@
sendKey(KEYCODE_POWER);
mPhoneWindowManager.assertNoPowerSleep();
}
+
+ /**
+ * Double press of power when the window handles the power key events. The
+ * system double power gesture launch should not be performed.
+ */
+ @Test
+ @EnableFlags(FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
+ public void testPowerDoublePress_windowHasOverridePermissionAndKeysHandled() {
+ mPhoneWindowManager.overrideCanWindowOverridePowerKey(true);
+ setDispatchedKeyHandler(keyEvent -> true);
+
+ sendKey(KEYCODE_POWER);
+ sendKey(KEYCODE_POWER);
+
+ mPhoneWindowManager.assertDidNotLockAfterAppTransitionFinished();
+
+ mPhoneWindowManager.assertNoDoublePowerLaunch();
+ }
+
+ /**
+ * Double press of power when the window doesn't handle the power key events.
+ * The system default gesture launch should be performed and the app should receive both events.
+ */
+ @Test
+ @EnableFlags(FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
+ public void testPowerDoublePress_windowHasOverridePermissionAndKeysUnHandled() {
+ mPhoneWindowManager.overrideCanWindowOverridePowerKey(true);
+ setDispatchedKeyHandler(keyEvent -> false);
+
+ sendKey(KEYCODE_POWER);
+ sendKey(KEYCODE_POWER);
+
+ mPhoneWindowManager.assertDidNotLockAfterAppTransitionFinished();
+ mPhoneWindowManager.assertDoublePowerLaunch();
+ assertEquals(getDownKeysDispatched(), 2);
+ assertEquals(getUpKeysDispatched(), 2);
+ }
+
+ /**
+ * Triple press of power when the window handles the power key double press gesture.
+ * The system default gesture launch should not be performed, and the app only receives the
+ * first two presses.
+ */
+ @Test
+ @EnableFlags(FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
+ public void testPowerTriplePress_windowHasOverridePermissionAndKeysHandled() {
+ mPhoneWindowManager.overrideCanWindowOverridePowerKey(true);
+ setDispatchedKeyHandler(keyEvent -> true);
+
+ sendKey(KEYCODE_POWER);
+ sendKey(KEYCODE_POWER);
+ sendKey(KEYCODE_POWER);
+
+ mPhoneWindowManager.assertDidNotLockAfterAppTransitionFinished();
+ mPhoneWindowManager.assertNoDoublePowerLaunch();
+ assertEquals(getDownKeysDispatched(), 2);
+ assertEquals(getUpKeysDispatched(), 2);
+ }
+
+ /**
+ * Tests a single press, followed by a double press when the window can handle the power key.
+ * The app should receive all 3 events.
+ */
+ @Test
+ @EnableFlags(FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
+ public void testPowerTriplePressWithDelay_windowHasOverridePermissionAndKeysHandled() {
+ mPhoneWindowManager.overrideCanWindowOverridePowerKey(true);
+ setDispatchedKeyHandler(keyEvent -> true);
+
+ sendKey(KEYCODE_POWER);
+ mPhoneWindowManager.moveTimeForward(POWER_MULTI_PRESS_TIMEOUT_MILLIS);
+ sendKey(KEYCODE_POWER);
+ sendKey(KEYCODE_POWER);
+
+ mPhoneWindowManager.assertNoDoublePowerLaunch();
+ assertEquals(getDownKeysDispatched(), 3);
+ assertEquals(getUpKeysDispatched(), 3);
+ }
+
+ /**
+ * Tests single press when window doesn't handle the power key. Phone should go to sleep.
+ */
+ @Test
+ @EnableFlags(FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
+ public void testPowerSinglePress_windowHasOverridePermissionAndKeyUnhandledByApp() {
+ mPhoneWindowManager.overrideCanWindowOverridePowerKey(true);
+ setDispatchedKeyHandler(keyEvent -> false);
+ mPhoneWindowManager.overrideShortPressOnPower(SHORT_PRESS_POWER_GO_TO_SLEEP);
+
+ sendKey(KEYCODE_POWER);
+
+ mPhoneWindowManager.assertPowerSleep();
+ }
+
+ /**
+ * Tests single press when the window handles the power key. Phone should go to sleep after a
+ * delay of {POWER_MULTI_PRESS_TIMEOUT_MILLIS}
+ */
+ @Test
+ @EnableFlags(FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
+ public void testPowerSinglePress_windowHasOverridePermissionAndKeyHandledByApp() {
+ mPhoneWindowManager.overrideCanWindowOverridePowerKey(true);
+ setDispatchedKeyHandler(keyEvent -> true);
+ mPhoneWindowManager.overrideDisplayState(Display.STATE_ON);
+ mPhoneWindowManager.overrideShortPressOnPower(SHORT_PRESS_POWER_GO_TO_SLEEP);
+
+ sendKey(KEYCODE_POWER);
+
+ mPhoneWindowManager.moveTimeForward(POWER_MULTI_PRESS_TIMEOUT_MILLIS);
+
+ mPhoneWindowManager.assertPowerSleep();
+ }
+
+
+ /**
+ * Tests 5x press when the window handles the power key. Emergency gesture should still be
+ * launched.
+ */
+ @Test
+ @EnableFlags(FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
+ public void testPowerFiveTimesPress_windowHasOverridePermissionAndKeyHandledByApp() {
+ mPhoneWindowManager.overrideCanWindowOverridePowerKey(true);
+ setDispatchedKeyHandler(keyEvent -> true);
+ mPhoneWindowManager.overrideDisplayState(Display.STATE_ON);
+ mPhoneWindowManager.overrideShortPressOnPower(SHORT_PRESS_POWER_GO_TO_SLEEP);
+
+ for (int i = 0; i < 5; ++i) {
+ sendKey(KEYCODE_POWER);
+ mPhoneWindowManager.moveTimeForward(100);
+ }
+
+ mPhoneWindowManager.assertEmergencyLaunch();
+ assertEquals(getDownKeysDispatched(), 2);
+ assertEquals(getUpKeysDispatched(), 2);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index 9e47a00..2329a07 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -85,7 +85,11 @@
private Resources mResources;
private PackageManager mPackageManager;
TestPhoneWindowManager mPhoneWindowManager;
- DispatchedKeyHandler mDispatchedKeyHandler = event -> false;
+
+ DispatchedKeyHandler mDispatchedKeyHandler;
+ private int mDownKeysDispatched;
+ private int mUpKeysDispatched;
+
Context mContext;
/** Modifier key to meta state */
@@ -116,6 +120,9 @@
XmlResourceParser testBookmarks = mResources.getXml(
com.android.frameworks.wmtests.R.xml.bookmarks);
doReturn(testBookmarks).when(mResources).getXml(com.android.internal.R.xml.bookmarks);
+ mDispatchedKeyHandler = event -> false;
+ mDownKeysDispatched = 0;
+ mUpKeysDispatched = 0;
try {
// Keep packageName / className in sync with
@@ -229,6 +236,10 @@
sendKeyCombination(new int[]{keyCode}, 0 /*durationMillis*/, longPress, DEFAULT_DISPLAY);
}
+ void sendKey(int keyCode, long durationMillis) {
+ sendKeyCombination(new int[]{keyCode}, durationMillis, false, DEFAULT_DISPLAY);
+ }
+
boolean sendKeyGestureEventStart(int gestureType) {
return mPhoneWindowManager.sendKeyGestureEvent(
new KeyGestureEvent.Builder().setKeyGestureType(gestureType).setAction(
@@ -278,6 +289,14 @@
doReturn(expectedBehavior).when(mResources).getInteger(eq(resId));
}
+ int getDownKeysDispatched() {
+ return mDownKeysDispatched;
+ }
+
+ int getUpKeysDispatched() {
+ return mUpKeysDispatched;
+ }
+
private void interceptKey(KeyEvent keyEvent) {
int actions = mPhoneWindowManager.interceptKeyBeforeQueueing(keyEvent);
if ((actions & ACTION_PASS_TO_USER) != 0) {
@@ -285,6 +304,11 @@
if (!mDispatchedKeyHandler.onKeyDispatched(keyEvent)) {
mPhoneWindowManager.interceptUnhandledKey(keyEvent);
}
+ if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
+ ++mDownKeysDispatched;
+ } else {
+ ++mUpKeysDispatched;
+ }
}
}
mPhoneWindowManager.dispatchAllPendingEvents();
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 9db76d4..f06b45e 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -22,6 +22,7 @@
import static android.view.Display.STATE_ON;
import static android.view.WindowManagerPolicyConstants.FLAG_INTERACTIVE;
+import static com.android.hardware.input.Flags.overridePowerKeyBehaviorInFocusedWindow;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong;
@@ -45,10 +46,14 @@
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAVIOR_MUTE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.description;
import static org.mockito.Mockito.mockingDetails;
import static org.mockito.Mockito.timeout;
@@ -85,7 +90,9 @@
import android.os.test.TestLooper;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
+import android.service.quickaccesswallet.QuickAccessWalletClient;
import android.telecom.TelecomManager;
+import android.util.MutableBoolean;
import android.view.Display;
import android.view.InputEvent;
import android.view.KeyCharacterMap;
@@ -95,9 +102,12 @@
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.internal.accessibility.AccessibilityShortcutController;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.policy.KeyInterceptionInfo;
import com.android.server.GestureLauncherService;
import com.android.server.LocalServices;
+import com.android.server.SystemService;
import com.android.server.input.InputManagerInternal;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.pm.UserManagerInternal;
@@ -120,6 +130,7 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
import java.util.List;
import java.util.function.Supplier;
@@ -132,6 +143,8 @@
private PhoneWindowManager mPhoneWindowManager;
private Context mContext;
+ private GestureLauncherService mGestureLauncherService;
+
@Mock private WindowManagerInternal mWindowManagerInternal;
@Mock private ActivityManagerInternal mActivityManagerInternal;
@@ -163,7 +176,9 @@
@Mock private DisplayRotation mDisplayRotation;
@Mock private DisplayPolicy mDisplayPolicy;
@Mock private WindowManagerPolicy.ScreenOnListener mScreenOnListener;
- @Mock private GestureLauncherService mGestureLauncherService;
+ @Mock private QuickAccessWalletClient mQuickAccessWalletClient;
+ @Mock private MetricsLogger mMetricsLogger;
+ @Mock private UiEventLogger mUiEventLogger;
@Mock private GlobalActions mGlobalActions;
@Mock private AccessibilityShortcutController mAccessibilityShortcutController;
@@ -192,6 +207,8 @@
private int mKeyEventPolicyFlags = FLAG_INTERACTIVE;
+ private int mProcessPowerKeyDownCount = 0;
+
private class TestTalkbackShortcutController extends TalkbackShortcutController {
TestTalkbackShortcutController(Context context) {
super(context);
@@ -260,6 +277,8 @@
MockitoAnnotations.initMocks(this);
mHandler = new Handler(mTestLooper.getLooper());
mContext = mockingDetails(context).isSpy() ? context : spy(context);
+ mGestureLauncherService = spy(new GestureLauncherService(mContext, mMetricsLogger,
+ mQuickAccessWalletClient, mUiEventLogger));
setUp(supportSettingsUpdate);
mTestLooper.dispatchAll();
}
@@ -272,6 +291,7 @@
mMockitoSession = mockitoSession()
.mockStatic(LocalServices.class, spyStubOnly)
.mockStatic(KeyCharacterMap.class)
+ .mockStatic(GestureLauncherService.class)
.strictness(Strictness.LENIENT)
.startMocking();
@@ -294,6 +314,16 @@
() -> LocalServices.getService(eq(PowerManagerInternal.class)));
doReturn(mDisplayManagerInternal).when(
() -> LocalServices.getService(eq(DisplayManagerInternal.class)));
+ doReturn(true).when(
+ () -> GestureLauncherService.isCameraDoubleTapPowerSettingEnabled(any(), anyInt())
+ );
+ doReturn(true).when(
+ () -> GestureLauncherService.isEmergencyGestureSettingEnabled(any(), anyInt())
+ );
+ doReturn(true).when(
+ () -> GestureLauncherService.isGestureLauncherEnabled(any())
+ );
+ mGestureLauncherService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
doReturn(mGestureLauncherService).when(
() -> LocalServices.getService(eq(GestureLauncherService.class)));
doReturn(mUserManagerInternal).when(
@@ -375,7 +405,8 @@
doNothing().when(mContext).startActivityAsUser(any(), any());
doNothing().when(mContext).startActivityAsUser(any(), any(), any());
- KeyInterceptionInfo interceptionInfo = new KeyInterceptionInfo(0, 0, null, 0);
+ KeyInterceptionInfo interceptionInfo = new KeyInterceptionInfo(0, 0, null, 0,
+ /* inputFeatureFlags = */ 0);
doReturn(interceptionInfo)
.when(mWindowManagerInternal).getKeyInterceptionInfoFromToken(any());
@@ -393,6 +424,8 @@
eq(TEST_BROWSER_ROLE_PACKAGE_NAME));
doReturn(mSmsIntent).when(mPackageManager).getLaunchIntentForPackage(
eq(TEST_SMS_ROLE_PACKAGE_NAME));
+ mProcessPowerKeyDownCount = 0;
+ captureProcessPowerKeyDownCount();
Mockito.reset(mContext);
}
@@ -638,6 +671,12 @@
.when(mButtonOverridePermissionChecker).canAppOverrideSystemKey(any(), anyInt());
}
+ void overrideCanWindowOverridePowerKey(boolean granted) {
+ doReturn(granted)
+ .when(mButtonOverridePermissionChecker).canWindowOverridePowerKey(any(), anyInt(),
+ anyInt());
+ }
+
void overrideKeyEventPolicyFlags(int flags) {
mKeyEventPolicyFlags = flags;
}
@@ -713,13 +752,59 @@
verify(mPowerManager, never()).goToSleep(anyLong(), anyInt(), anyInt());
}
- void assertCameraLaunch() {
+ void assertDoublePowerLaunch() {
+ ArgumentCaptor<MutableBoolean> valueCaptor = ArgumentCaptor.forClass(MutableBoolean.class);
+
mTestLooper.dispatchAll();
- // GestureLauncherService should receive interceptPowerKeyDown twice.
- verify(mGestureLauncherService, times(2))
- .interceptPowerKeyDown(any(), anyBoolean(), any());
+ verify(mGestureLauncherService, atLeast(2))
+ .interceptPowerKeyDown(any(), anyBoolean(), valueCaptor.capture());
+ verify(mGestureLauncherService, atMost(4))
+ .interceptPowerKeyDown(any(), anyBoolean(), valueCaptor.capture());
+
+ if (overridePowerKeyBehaviorInFocusedWindow()) {
+ assertTrue(mProcessPowerKeyDownCount >= 2 && mProcessPowerKeyDownCount <= 4);
+ }
+
+ List<Boolean> capturedValues = valueCaptor.getAllValues().stream()
+ .map(mutableBoolean -> mutableBoolean.value)
+ .toList();
+
+ assertTrue(capturedValues.contains(true));
}
+ void assertNoDoublePowerLaunch() {
+ ArgumentCaptor<MutableBoolean> valueCaptor = ArgumentCaptor.forClass(MutableBoolean.class);
+
+ mTestLooper.dispatchAll();
+ verify(mGestureLauncherService, atLeast(0))
+ .interceptPowerKeyDown(any(), anyBoolean(), valueCaptor.capture());
+
+ List<Boolean> capturedValues = valueCaptor.getAllValues().stream()
+ .map(mutableBoolean -> mutableBoolean.value)
+ .toList();
+
+ assertTrue(capturedValues.stream().noneMatch(value -> value));
+ }
+
+ void assertEmergencyLaunch() {
+ ArgumentCaptor<MutableBoolean> valueCaptor = ArgumentCaptor.forClass(MutableBoolean.class);
+
+ mTestLooper.dispatchAll();
+ verify(mGestureLauncherService, atLeast(1))
+ .interceptPowerKeyDown(any(), anyBoolean(), valueCaptor.capture());
+
+ if (overridePowerKeyBehaviorInFocusedWindow()) {
+ assertEquals(mProcessPowerKeyDownCount, 5);
+ }
+
+ List<Boolean> capturedValues = valueCaptor.getAllValues().stream()
+ .map(mutableBoolean -> mutableBoolean.value)
+ .toList();
+
+ assertTrue(capturedValues.getLast());
+ }
+
+
void assertSearchManagerLaunchAssist() {
mTestLooper.dispatchAll();
verify(mSearchManager).launchAssist(any());
@@ -929,4 +1014,12 @@
verify(mInputManagerInternal)
.handleKeyGestureInKeyGestureController(anyInt(), any(), anyInt(), eq(gestureType));
}
+
+ private void captureProcessPowerKeyDownCount() {
+ doAnswer((Answer<Void>) invocation -> {
+ invocation.callRealMethod();
+ mProcessPowerKeyDownCount++;
+ return null;
+ }).when(mGestureLauncherService).processPowerKeyDown(any());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
index 2a53df9..a7fc10f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
@@ -17,30 +17,23 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.SnapshotPersistQueue.MAX_STORE_QUEUE_DEPTH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import android.content.ComponentName;
-import android.graphics.ColorSpace;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.HardwareBuffer;
import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
-import android.view.Surface;
import android.window.TaskSnapshot;
import androidx.test.filters.SmallTest;
@@ -61,14 +54,20 @@
@SmallTest
@Presubmit
@RunWith(WindowTestRunner.class)
-public class ActivitySnapshotControllerTests extends WindowTestsBase {
+public class ActivitySnapshotControllerTests extends TaskSnapshotPersisterTestBase {
private ActivitySnapshotController mActivitySnapshotController;
+ public ActivitySnapshotControllerTests() {
+ super(0.8f /* highResScale */, 0.5f /* lowResScale */);
+ }
+
+ @Override
@Before
- public void setUp() throws Exception {
- spyOn(mWm.mSnapshotController.mActivitySnapshotController);
- mActivitySnapshotController = mWm.mSnapshotController.mActivitySnapshotController;
+ public void setUp() {
+ super.setUp();
+ mActivitySnapshotController = new ActivitySnapshotController(mWm, mSnapshotPersistQueue);
+ spyOn(mActivitySnapshotController);
doReturn(false).when(mActivitySnapshotController).shouldDisableSnapshots();
mActivitySnapshotController.resetTmpFields();
}
@@ -92,12 +91,11 @@
assertEquals(0, mActivitySnapshotController.mPendingRemoveActivity.size());
mActivitySnapshotController.resetTmpFields();
- // simulate three activity
+ // simulate three activity, the bottom activity won't participate in transition
final WindowState belowClose = createAppWindow(task, ACTIVITY_TYPE_STANDARD,
"belowClose");
belowClose.mActivityRecord.commitVisibility(
false /* visible */, true /* performLayout */);
- windows.add(belowClose.mActivityRecord);
mActivitySnapshotController.handleTransitionFinish(windows);
assertEquals(1, mActivitySnapshotController.mPendingRemoveActivity.size());
assertEquals(belowClose.mActivityRecord,
@@ -249,15 +247,28 @@
assertEquals(taskSnapshot, mActivitySnapshotController.getSnapshot(activities));
}
- private TaskSnapshot createSnapshot() {
- HardwareBuffer buffer = mock(HardwareBuffer.class);
- doReturn(100).when(buffer).getWidth();
- doReturn(100).when(buffer).getHeight();
- return new TaskSnapshot(1, 0 /* captureTime */, new ComponentName("", ""), buffer,
- ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
- Surface.ROTATION_0, new Point(100, 100), new Rect() /* contentInsets */,
- new Rect() /* letterboxInsets*/, false /* isLowResolution */,
- true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN, 0 /* mSystemUiVisibility */,
- false /* isTranslucent */, false /* hasImeSurface */, 0 /* uiMode */);
+ /**
+ * Verifies that activity snapshot is skipped if the persister queue has too many pending write
+ * items.
+ */
+ @Test
+ public void testSkipRecordActivity() {
+ doReturn(createSnapshot()).when(mActivitySnapshotController).recordSnapshotInner(any());
+ final Task task = createTask(mDisplayContent);
+
+ mSnapshotPersistQueue.setPaused(true);
+ final ArrayList<ActivityRecord> tmpList = new ArrayList<>();
+ for (int i = 0; i < MAX_STORE_QUEUE_DEPTH; ++i) {
+ tmpList.clear();
+ final ActivityRecord activity = createActivityRecord(task);
+ tmpList.add(activity);
+ mActivitySnapshotController.recordSnapshot(tmpList);
+ assertNotNull(mActivitySnapshotController.findSavedFile(activity));
+ }
+ tmpList.clear();
+ final ActivityRecord activity = createActivityRecord(task);
+ tmpList.add(activity);
+ mActivitySnapshotController.recordSnapshot(tmpList);
+ assertNull(mActivitySnapshotController.findSavedFile(activity));
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
index 2ceff62..3750dd3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
@@ -31,6 +31,9 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -46,17 +49,20 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.CameraCompatTaskInfo;
+import android.app.IApplicationThread;
import android.app.WindowConfiguration.WindowingMode;
import android.app.servertransaction.RefreshCallbackItem;
import android.app.servertransaction.ResumeActivityItem;
import android.compat.testing.PlatformCompatChangeRule;
import android.content.ComponentName;
import android.content.pm.ActivityInfo.ScreenOrientation;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Configuration.Orientation;
import android.graphics.Rect;
@@ -77,6 +83,7 @@
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import java.util.concurrent.Executor;
@@ -138,7 +145,7 @@
mCameraCompatFreeformPolicy = new CameraCompatFreeformPolicy(mDisplayContent,
cameraStateMonitor, mActivityRefresher);
- setDisplayRotation(Surface.ROTATION_90);
+ setDisplayRotation(ROTATION_90);
mCameraCompatFreeformPolicy.start();
cameraStateMonitor.startListeningToCameraState();
}
@@ -229,7 +236,7 @@
@EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testCameraConnected_deviceInPortrait_portraitCameraCompatMode() throws Exception {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
- setDisplayRotation(Surface.ROTATION_0);
+ setDisplayRotation(ROTATION_0);
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_PORTRAIT);
@@ -241,7 +248,7 @@
@EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testCameraConnected_deviceInLandscape_portraitCameraCompatMode() throws Exception {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
- setDisplayRotation(Surface.ROTATION_270);
+ setDisplayRotation(ROTATION_270);
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE);
@@ -253,7 +260,7 @@
@EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testCameraConnected_deviceInPortrait_landscapeCameraCompatMode() throws Exception {
configureActivity(SCREEN_ORIENTATION_LANDSCAPE);
- setDisplayRotation(Surface.ROTATION_0);
+ setDisplayRotation(ROTATION_0);
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_PORTRAIT);
@@ -265,7 +272,7 @@
@EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testCameraConnected_deviceInLandscape_landscapeCameraCompatMode() throws Exception {
configureActivity(SCREEN_ORIENTATION_LANDSCAPE);
- setDisplayRotation(Surface.ROTATION_270);
+ setDisplayRotation(ROTATION_270);
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_LANDSCAPE);
@@ -277,7 +284,7 @@
@EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testCameraReconnected_cameraCompatModeAndRefresh() throws Exception {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
- setDisplayRotation(Surface.ROTATION_270);
+ setDisplayRotation(ROTATION_270);
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
callOnActivityConfigurationChanging(mActivity, /* letterboxNew= */ true,
@@ -467,6 +474,36 @@
/* delta= */ 0.001);
}
+ @Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+ public void testOnCameraOpened_portraitActivity_sandboxesDisplayRotationAndUpdatesApp() throws
+ Exception {
+ configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+ setDisplayRotation(ROTATION_270);
+
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+ // This is a portrait rotation for a device with portrait natural orientation (most common,
+ // currently the only one supported).
+ assertCompatibilityInfoSentWithDisplayRotation(ROTATION_0);
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+ public void testOnCameraOpened_landscapeActivity_sandboxesDisplayRotationAndUpdatesApp() throws
+ Exception {
+ configureActivity(SCREEN_ORIENTATION_LANDSCAPE);
+ setDisplayRotation(ROTATION_0);
+
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+ // This is a landscape rotation for a device with portrait natural orientation (most common,
+ // currently the only one supported).
+ assertCompatibilityInfoSentWithDisplayRotation(ROTATION_90);
+ }
+
private void configureActivity(@ScreenOrientation int activityOrientation) {
configureActivity(activityOrientation, WINDOWING_MODE_FREEFORM);
}
@@ -499,6 +536,7 @@
doReturn(windowingMode == WINDOWING_MODE_FREEFORM).when(mActivity)
.inFreeformWindowingMode();
+ setupMockApplicationThread();
}
private void assertInCameraCompatMode(@CameraCompatTaskInfo.FreeformCameraCompatMode int mode) {
@@ -557,9 +595,27 @@
// case for most standard phones and tablets.
// TODO(b/365725400): handle landscape natural orientation.
displayInfo.logicalHeight = displayRotation % 180 == 0 ? 800 : 600;
- displayInfo.logicalWidth = displayRotation % 180 == 0 ? 600 : 800;
+ displayInfo.logicalWidth = displayRotation % 180 == 0 ? 600 : 800;
return displayInfo;
}).when(mDisplayContent.mWmService.mDisplayManagerInternal)
.getDisplayInfo(anyInt());
}
+
+ private void setupMockApplicationThread() {
+ IApplicationThread mockApplicationThread = mock(IApplicationThread.class);
+ spyOn(mActivity.app);
+ doReturn(mockApplicationThread).when(mActivity.app).getThread();
+ }
+
+ private void assertCompatibilityInfoSentWithDisplayRotation(@Surface.Rotation int
+ expectedRotation) throws Exception {
+ final ArgumentCaptor<CompatibilityInfo> compatibilityInfoArgumentCaptor =
+ ArgumentCaptor.forClass(CompatibilityInfo.class);
+ verify(mActivity.app.getThread()).updatePackageCompatibilityInfo(eq(mActivity.packageName),
+ compatibilityInfoArgumentCaptor.capture());
+
+ final CompatibilityInfo compatInfo = compatibilityInfoArgumentCaptor.getValue();
+ assertTrue(compatInfo.isOverrideDisplayRotationRequired());
+ assertEquals(expectedRotation, compatInfo.applicationDisplayRotation);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index aa99250..25b9f4b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -717,13 +717,13 @@
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
+ @DisableFlags(Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
public void testVisibleTasks_excludedFromRecents() {
testVisibleTasks_excludedFromRecents_internal();
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
+ @EnableFlags(Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
public void testVisibleTasks_excludedFromRecents_withRefactorFlag() {
testVisibleTasks_excludedFromRecents_internal();
}
@@ -767,13 +767,13 @@
@Test
@Ignore("b/342627272")
- @DisableFlags(Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
+ @DisableFlags(Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
public void testVisibleTasks_excludedFromRecents_visibleTaskNotFirstTask() {
testVisibleTasks_excludedFromRecents_visibleTaskNotFirstTask_internal();
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
+ @EnableFlags(Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
public void testVisibleTasks_excludedFromRecents_visibleTaskNotFirstTask_withRefactorFlag() {
testVisibleTasks_excludedFromRecents_visibleTaskNotFirstTask_internal();
}
@@ -816,13 +816,13 @@
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
+ @DisableFlags(Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
public void testVisibleTasks_excludedFromRecents_firstTaskNotVisible() {
testVisibleTasks_excludedFromRecents_firstTaskNotVisible_internal();
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
+ @EnableFlags(Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK)
public void testVisibleTasks_excludedFromRecents_firstTaskNotVisible_withRefactorFlag() {
testVisibleTasks_excludedFromRecents_firstTaskNotVisible_internal();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
index d183cf7..f3a2e86 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
@@ -46,6 +46,7 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.DisplayInfo;
@@ -54,7 +55,12 @@
import androidx.test.filters.SmallTest;
+import com.android.window.flags.Flags;
+
+import com.google.common.truth.Expect;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -65,6 +71,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+
/**
* Build/Install/Run:
* atest WmTests:WindowContextListenerControllerTests
@@ -86,6 +93,9 @@
private WindowProcessController mWpc;
private WindowContainer<?> mContainer;
+ @Rule
+ public final Expect mExpect = Expect.create();
+
@Before
public void setUp() {
initMocks(this);
@@ -341,6 +351,30 @@
assertThat(clientToken.mDisplayId).isEqualTo(mDisplayContent.mDisplayId);
}
+ @Test
+ @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
+ public void assertCallerCanReparentListener_returnsTrueWhenExpected() {
+ mController.registerWindowContainerListener(mWpc, mClientToken, mContainer,
+ TYPE_APPLICATION_OVERLAY, null /* options */);
+
+ // Here there are several checks in one test as wm tests are expensive.
+
+ // Correct conditions -> returns true
+ mExpect.that(mController.assertCallerCanReparentListener(mClientToken,
+ /* callerCanManageAppTokens= */ true,
+ /* callingUid= */ mWpc.mUid,
+ /* displayId= */ DEFAULT_DISPLAY + 1
+ )).isTrue();
+
+ // sameDisplayId (so, container already attached) -> returnsFalse
+ mExpect.that(mController.assertCallerCanReparentListener(
+ mClientToken,
+ /* callerCanManageAppTokens= */ true,
+ /* callingUid= */ mWpc.mUid,
+ /* displayId= */ DEFAULT_DISPLAY // <- same display ID
+ )).isFalse();
+ }
+
private static class TestWindowTokenClient extends WindowTokenClient {
private Configuration mConfiguration;
private int mDisplayId;
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 bfa6cb8..69df66e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -39,6 +39,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
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_NOTIFICATION_SHADE;
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;
@@ -70,6 +71,7 @@
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.description;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -86,10 +88,10 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
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.util.ArraySet;
import android.util.MergedConfiguration;
import android.view.ContentRecordingSession;
@@ -151,9 +153,6 @@
@Rule
public Expect mExpect = Expect.create();
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
@After
public void tearDown() {
mWm.mSensitiveContentPackages.clearBlockedApps();
@@ -1412,6 +1411,84 @@
assertThat(result).isEqualTo(WindowManagerGlobal.ADD_INVALID_DISPLAY);
}
+ /** Mocks some deps to associate a display content to a specific display id. */
+ private void setupReparentWindowContextToDisplayAreaTest(WindowToken windowToken,
+ DisplayContent dc, int displayId) {
+ spyOn(mWm.mWindowContextListenerController);
+ doReturn(dc).when(mWm.mRoot).getDisplayContentOrCreate(displayId);
+ doReturn(true).when(mWm.mWindowContextListenerController).assertCallerCanReparentListener(
+ any(), anyBoolean(), anyInt(), eq(displayId));
+ doReturn(windowToken).when(mWm.mWindowContextListenerController).getContainer(
+ eq(windowToken.token));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
+ public void reparentWindowContextToDisplayArea_newDisplay_reparented() {
+ final WindowToken windowToken = createTestClientWindowToken(TYPE_NOTIFICATION_SHADE,
+ mDisplayContent);
+ final int newDisplayId = 1;
+ final DisplayContent dc = createNewDisplay();
+ setupReparentWindowContextToDisplayAreaTest(windowToken, dc, newDisplayId);
+
+ assertThat(windowToken.getDisplayContent()).isEqualTo(mDisplayContent);
+
+ assertThat(mWm.reparentWindowContextToDisplayArea(mAppThread, windowToken.token,
+ newDisplayId)).isTrue();
+
+ assertThat(windowToken.getDisplayContent()).isNotEqualTo(mDisplayContent);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
+ public void reparentWindowContextToDisplayArea_newDisplayButFlagDisabled_notReparented() {
+ final WindowToken windowToken = createTestClientWindowToken(TYPE_NOTIFICATION_SHADE,
+ mDisplayContent);
+ final int newDisplayId = 1;
+ final DisplayContent dc = createNewDisplay();
+ setupReparentWindowContextToDisplayAreaTest(windowToken, dc, newDisplayId);
+
+ assertThat(mWm.reparentWindowContextToDisplayArea(mAppThread, windowToken.token,
+ newDisplayId)).isFalse();
+
+ assertThat(windowToken.getDisplayContent()).isEqualTo(mDisplayContent);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
+ public void reparentWindowContext_afterReparent_DCNeedsLayout() {
+ final WindowToken windowToken = createTestClientWindowToken(TYPE_NOTIFICATION_SHADE,
+ mDisplayContent);
+ final int newDisplayId = 1;
+ final DisplayContent dc = createNewDisplay();
+ setupReparentWindowContextToDisplayAreaTest(windowToken, dc, newDisplayId);
+
+ assertThat(mWm.reparentWindowContextToDisplayArea(mAppThread, windowToken.token,
+ newDisplayId)).isTrue();
+
+ assertThat(dc.isLayoutNeeded()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
+ public void reparentWindowContext_afterReparent_traversalScheduled() {
+ final WindowToken windowToken = createTestClientWindowToken(TYPE_NOTIFICATION_SHADE,
+ mDisplayContent);
+ final int newDisplayId = 1;
+ final DisplayContent dc = createNewDisplay();
+ setupReparentWindowContextToDisplayAreaTest(windowToken, dc, newDisplayId);
+ spyOn(mWm.mWindowPlacerLocked);
+ reset(mWm.mWindowPlacerLocked);
+
+ verify(mWm.mWindowPlacerLocked, never()).requestTraversal();
+
+ assertThat(mWm.reparentWindowContextToDisplayArea(mAppThread, windowToken.token,
+ newDisplayId)).isTrue();
+
+ verify(mWm.mWindowPlacerLocked).requestTraversal();
+ }
+
+
class TestResultReceiver implements IResultReceiver {
public android.os.Bundle resultData;
private final IBinder mBinder = mock(IBinder.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 78e6cbf..3a97cc6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -2039,9 +2039,22 @@
return new TestWindowToken(type, dc, persistOnEmpty);
}
+ static TestWindowToken createTestClientWindowToken(int type, DisplayContent dc) {
+ SystemServicesTestRule.checkHoldsLock(dc.mWmService.mGlobalLock);
+
+ return new TestWindowToken(type, dc, false /* persistOnEmpty */, true /* fromClient */);
+ }
+
/** Used so we can gain access to some protected members of the {@link WindowToken} class */
static class TestWindowToken extends WindowToken {
+ private TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty,
+ boolean fromClient) {
+ super(dc.mWmService, mock(IBinder.class), type, persistOnEmpty, dc,
+ false /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
+ fromClient /* fromClientToken */, null /* options */);
+ }
+
private TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) {
super(dc.mWmService, mock(IBinder.class), type, persistOnEmpty, dc,
false /* ownerCanManageAppTokens */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index 35328a0e..f226b9d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -33,21 +33,27 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.view.WindowInsets;
import android.window.WindowContext;
import androidx.test.filters.SmallTest;
+import com.android.window.flags.Flags;
+
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mockito;
import java.util.function.BiFunction;
@@ -304,14 +310,39 @@
// immediately. verify the window will hide without applying exit animation.
mWm.removeWindowToken(win.mToken.token, false /* removeWindows */, false /* animateExit */,
mDisplayContent.mDisplayId);
- verify(win).onSetAppExiting(Mockito.eq(false) /* animateExit */);
+ verify(win).onSetAppExiting(eq(false) /* animateExit */);
verify(win).hide(false /* doAnimation */, false /* requestAnim */);
assertFalse(win.isOnScreen());
- verify(win.mWinAnimator, Mockito.never()).applyAnimationLocked(TRANSIT_EXIT, false);
+ verify(win.mWinAnimator, never()).applyAnimationLocked(TRANSIT_EXIT, false);
assertTrue(win.mToken.hasChild());
// Even though the window is being removed afterwards, it won't apply exit animation.
win.removeIfPossible();
- verify(win.mWinAnimator, Mockito.never()).applyAnimationLocked(TRANSIT_EXIT, false);
+ verify(win.mWinAnimator, never()).applyAnimationLocked(TRANSIT_EXIT, false);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
+ public void onDisplayChanged_differentDisplay_reparented() {
+ final TestWindowToken token = createTestWindowToken(0, mDisplayContent);
+ final DisplayContent dc = mock(DisplayContent.class);
+ when(dc.getWindowToken(any())).thenReturn(null); // dc doesn't have this window token.
+
+ token.onDisplayChanged(dc);
+
+ verify(dc).reParentWindowToken(eq(token));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
+ public void onDisplayChanged_samedisplay_notReparented() {
+
+ final TestWindowToken token = createTestWindowToken(0, mDisplayContent);
+ final DisplayContent dc = mock(DisplayContent.class);
+ when(dc.getWindowToken(any())).thenReturn(mock(WindowToken.class));
+
+ token.onDisplayChanged(dc);
+
+ verify(dc, never()).reParentWindowToken(eq(token));
}
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 010a322..e7c9e92 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -48,6 +48,7 @@
import android.app.PendingIntent;
import android.app.UidObserver;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.supervision.SupervisionManagerInternal;
import android.app.usage.AppLaunchEstimateInfo;
import android.app.usage.AppStandbyInfo;
import android.app.usage.BroadcastResponseStatsList;
@@ -230,6 +231,7 @@
// Do not use directly. Call getDpmInternal() instead
DevicePolicyManagerInternal mDpmInternal;
// Do not use directly. Call getShortcutServiceInternal() instead
+ SupervisionManagerInternal mSupervisionManagerInternal;
ShortcutServiceInternal mShortcutServiceInternal;
private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>();
@@ -439,6 +441,9 @@
// initialize mDpmInternal
getDpmInternal();
// initialize mShortcutServiceInternal
+ if (android.app.supervision.flags.Flags.deprecateDpmSupervisionApis()) {
+ getSupervisionManagerInternal();
+ }
getShortcutServiceInternal();
mResponseStatsTracker.onSystemServicesReady(getContext());
@@ -604,6 +609,15 @@
return mDpmInternal;
}
+ @Nullable
+ private SupervisionManagerInternal getSupervisionManagerInternal() {
+ if (mSupervisionManagerInternal == null) {
+ mSupervisionManagerInternal =
+ LocalServices.getService(SupervisionManagerInternal.class);
+ }
+ return mSupervisionManagerInternal;
+ }
+
private ShortcutServiceInternal getShortcutServiceInternal() {
if (mShortcutServiceInternal == null) {
mShortcutServiceInternal = LocalServices.getService(ShortcutServiceInternal.class);
@@ -753,6 +767,16 @@
callingPid, callingUid) == PackageManager.PERMISSION_GRANTED);
}
+ private boolean isSupervisionEnabled(int callingUid) {
+ if (android.app.supervision.flags.Flags.deprecateDpmSupervisionApis()) {
+ SupervisionManagerInternal smInternal = getSupervisionManagerInternal();
+ return smInternal != null && smInternal.isActiveSupervisionApp(callingUid);
+ } else {
+ DevicePolicyManagerInternal dpmInternal = getDpmInternal();
+ return dpmInternal != null && dpmInternal.isActiveSupervisionApp(callingUid);
+ }
+ }
+
private static void deleteRecursively(final File path) {
if (path.isDirectory()) {
final File[] files = path.listFiles();
@@ -2929,10 +2953,9 @@
long timeLimitMs, long timeUsedMs, PendingIntent callbackIntent,
String callingPackage) {
final int callingUid = Binder.getCallingUid();
- final DevicePolicyManagerInternal dpmInternal = getDpmInternal();
if (!hasPermissions(
Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)
- && (dpmInternal == null || !dpmInternal.isActiveSupervisionApp(callingUid))) {
+ && !isSupervisionEnabled(callingUid)) {
throw new SecurityException("Caller must be the active supervision app or "
+ "it must have both SUSPEND_APPS and OBSERVE_APP_USAGE permissions");
}
@@ -2956,10 +2979,9 @@
@Override
public void unregisterAppUsageLimitObserver(int observerId, String callingPackage) {
final int callingUid = Binder.getCallingUid();
- final DevicePolicyManagerInternal dpmInternal = getDpmInternal();
if (!hasPermissions(
Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)
- && (dpmInternal == null || !dpmInternal.isActiveSupervisionApp(callingUid))) {
+ && !isSupervisionEnabled(callingUid)) {
throw new SecurityException("Caller must be the active supervision app or "
+ "it must have both SUSPEND_APPS and OBSERVE_APP_USAGE permissions");
}
diff --git a/telecomm/java/android/telecom/ParcelableCallAnalytics.java b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
index a69dfb0b..cdb3eaf 100644
--- a/telecomm/java/android/telecom/ParcelableCallAnalytics.java
+++ b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
@@ -16,12 +16,12 @@
package android.telecom;
+import android.annotation.FlaggedApi;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
-import java.util.LinkedList;
import java.util.List;
/**
@@ -255,6 +255,11 @@
public static final int CALLTYPE_OUTGOING = 2;
// Constants for call technology
+ /**
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(com.android.internal.telephony.flags.Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_PHONE = 0x1;
public static final int GSM_PHONE = 0x2;
public static final int IMS_PHONE = 0x4;
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index d89c9c1..7082f00 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -2049,11 +2049,15 @@
/**
* Ends the foreground call on the device.
* <p>
- * If there is a ringing call, calling this method rejects the ringing call. Otherwise the
+ * If there is a ringing call, calling this method rejects the ringing call. Otherwise, the
* foreground call is ended.
* <p>
* Note: this method CANNOT be used to end ongoing emergency calls and will return {@code false}
* if an attempt is made to end an emergency call.
+ * <p>
+ * Note: If the foreground call on this device is self-managed, this method will only end
+ * the call if the caller of this method is privileged (i.e. system, shell, or root) or system
+ * UI.
*
* @return {@code true} if there is a call which will be rejected or terminated, {@code false}
* otherwise.
@@ -2082,6 +2086,9 @@
* the incoming call requests. This means, for example, that an incoming call requesting
* {@link VideoProfile#STATE_BIDIRECTIONAL} will be answered, accepting that state.
*
+ * If the ringing incoming call is self-managed, this method will only accept the call if the
+ * caller of this method is privileged (i.e. system, shell, or root) or system UI.
+ *
* @deprecated Companion apps for wearable devices should use the {@link InCallService} API
* instead.
*/
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomLoader.aidl b/telecomm/java/com/android/internal/telecom/ITelecomLoader.aidl
index eda0f5b..c4a3670 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomLoader.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomLoader.aidl
@@ -24,5 +24,5 @@
* Allows the TelecomLoaderService to pass additional dependencies required for creation.
*/
interface ITelecomLoader {
- ITelecomService createTelecomService(IInternalServiceRetriever retriever);
+ ITelecomService createTelecomService(IInternalServiceRetriever retriever, String sysUiName);
}
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index 8fe107c..09b18b6 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -109,6 +109,7 @@
//TelephonyManager.NETWORK_TYPE_LTE_CA,
TelephonyManager.NETWORK_TYPE_NR,
+ TelephonyManager.NETWORK_TYPE_NB_IOT_NTN,
})
@Retention(RetentionPolicy.SOURCE)
public @interface NetworkType {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7cfdec6..478ec5c 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -317,8 +317,10 @@
* If this is set as false and the supplementary service menu is visible, the associated setting
* will be enabled and disabled based on the availability of supplementary services over UT. See
* {@link #KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL}.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final String KEY_SUPPORT_SS_OVER_CDMA_BOOL = "support_ss_over_cdma_bool";
/**
@@ -536,7 +538,11 @@
*/
public static final String KEY_4G_ONLY_BOOL = "4g_only_bool";
- /** Show cdma network mode choices 1x, 3G, global etc. */
+ /** Show cdma network mode choices 1x, 3G, global etc.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @Deprecated
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
public static final String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
/** CDMA activation goes through HFA */
@@ -544,9 +550,12 @@
/**
* CDMA activation goes through OTASP.
+ * @deprecated Legacy CDMA is unsupported.
*/
// TODO: This should be combined with config_use_hfa_for_provisioning and implemented as an enum
// (NONE, HFA, OTASP).
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final String KEY_USE_OTASP_FOR_PROVISIONING_BOOL =
"use_otasp_for_provisioning_bool";
@@ -556,10 +565,20 @@
/** Does not display additional call setting for IMS phone based on GSM Phone */
public static final String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
- /** Show APN Settings for some CDMA carriers */
+ /**
+ * Show APN Settings for some CDMA carriers
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @Deprecated
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
- /** After a CDMA conference call is merged, the swap button should be displayed. */
+ /**
+ * After a CDMA conference call is merged, the swap button should be displayed.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @Deprecated
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
public static final String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
/**
@@ -597,7 +616,10 @@
/**
* Disables dialing "*228" (OTASP provisioning) on CDMA carriers where it is not supported or is
* potentially harmful by locking the SIM to 3G.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @Deprecated
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
public static final String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL =
"disable_cdma_activation_code_bool";
@@ -675,14 +697,20 @@
/**
* Override the platform's notion of a network operator being considered roaming.
* Value is string array of SIDs to be considered roaming for 3GPP2 RATs.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final String
KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
/**
* Override the platform's notion of a network operator being considered non roaming.
* Value is string array of SIDs to be considered not roaming for 3GPP2 RATs.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final String
KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
@@ -1243,8 +1271,10 @@
/**
* CDMA carrier ERI (Enhanced Roaming Indicator) file name
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final String KEY_CARRIER_ERI_FILE_NAME_STRING = "carrier_eri_file_name_string";
/* The following 3 fields are related to carrier visual voicemail. */
@@ -1386,7 +1416,10 @@
* Specifies the amount of gap to be added in millis between postdial DTMF tones. When a
* non-zero value is specified, the UE shall wait for the specified amount of time before it
* sends out successive DTMF tones on the network.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
/**
@@ -1804,8 +1837,10 @@
* If this bit is not set, the carrier name display string will be selected from the carrier
* display name resolver which doesn't apply the ERI rules.
*
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final String KEY_ALLOW_ERI_BOOL = "allow_cdma_eri_bool";
/**
@@ -1849,8 +1884,10 @@
* If true, then the registered PLMN name (only for CDMA/CDMA-LTE and only when not roaming)
* will be #KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING. If false, or if phone type is not
* CDMA/CDMA-LTE or if roaming, then #KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING will be ignored.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final String KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL =
"cdma_home_registered_plmn_name_override_bool";
@@ -1858,8 +1895,10 @@
* String to identify registered PLMN name in CarrierConfig app. This string overrides
* registered PLMN name if #KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL is true, phone type
* is CDMA/CDMA-LTE and device is not in roaming state; otherwise, it will be ignored.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final String KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING =
"cdma_home_registered_plmn_name_string";
@@ -2440,7 +2479,10 @@
* For carriers which require an empty flash to be sent before sending the normal 3-way calling
* flash, the duration in milliseconds of the empty flash to send. When {@code 0}, no empty
* flash is sent.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int";
/**
@@ -2454,14 +2496,21 @@
* @see TelephonyManager#CDMA_ROAMING_MODE_HOME
* @see TelephonyManager#CDMA_ROAMING_MODE_AFFILIATED
* @see TelephonyManager#CDMA_ROAMING_MODE_ANY
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
/**
* Determines whether 1X voice calls is supported for some CDMA carriers.
* Default value is true.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
public static final String KEY_SUPPORT_CDMA_1X_VOICE_CALLS_BOOL =
"support_cdma_1x_voice_calls_bool";
@@ -2483,8 +2532,10 @@
/**
* Report IMEI as device id even if it's a CDMA/LTE phone.
*
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final String KEY_FORCE_IMEI_BOOL = "force_imei_bool";
/**
@@ -3217,8 +3268,10 @@
* on a 3GPP network. Specifically *67<number> will be converted to #31#<number> and
* *82<number> will be converted to *31#<number> before dialing a call when this key is
* set TRUE and device is roaming on a 3GPP network.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final String KEY_CONVERT_CDMA_CALLER_ID_MMI_CODES_WHILE_ROAMING_ON_3GPP_BOOL =
"convert_cdma_caller_id_mmi_codes_while_roaming_on_3gpp_bool";
@@ -3621,8 +3674,11 @@
/**
* Support for the original string display of CDMA MO call.
* By default, it is disabled.
+ *
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final String KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL =
"config_show_orig_dial_string_for_cdma";
@@ -5070,8 +5126,10 @@
* The default values come from 3GPP2 C.R1001 table 8.1-1.
* Enhanced Roaming Indicator Number Assignments
*
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final String KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY =
"cdma_enhanced_roaming_indicator_for_home_network_int_array";
@@ -9756,9 +9814,8 @@
* }</pre>
* <p>
* This config is empty by default.
- * @hide
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String KEY_REGIONAL_SATELLITE_EARFCN_BUNDLE =
"regional_satellite_earfcn_bundle";
@@ -9885,15 +9942,14 @@
"remove_satellite_plmn_in_manual_network_scan_bool";
/**
- * This value is used to set the max datagram size, if the value is not available then the
- * default one will be used.
- * If key is {@code true}, retrieve the max datagram value and use this value always,
- * {@code false} the default value from the modem will be used.
+ * This value is used to set the max datagram size in bytes.
+ * If the value is not available then the default value will be used.
*
- * @hide
+ * The default value is 255 bytes.
*/
- public static final String KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE =
- "satellite_sos_max_datagram_size";
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+ public static final String KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE_BYTES_INT =
+ "satellite_sos_max_datagram_size_bytes_int";
/** @hide */
@IntDef({
@@ -10061,6 +10117,13 @@
"satellite_nidd_apn_name_string";
/**
+ * The display name that will be used for satellite functionality within the UI.
+ * The default string value is empty string.
+ */
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+ public static final String KEY_SATELLITE_DISPLAY_NAME_STRING = "satellite_display_name_string";
+
+ /**
* Default value {@code true}, meaning when an emergency call request comes in, if the device is
* in emergency satellite mode but hasn't sent the first satellite datagram, then exits
* satellite mode to allow the emergency call to go through.
@@ -10176,10 +10239,8 @@
* A string array containing the list of messaging apps that support satellite.
*
* The default value contains only "com.google.android.apps.messaging"
- *
- * @hide
*/
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String KEY_SATELLITE_SUPPORTED_MSG_APPS_STRING_ARRAY =
"satellite_supported_msg_apps_string_array";
@@ -11343,6 +11404,7 @@
sDefaults.putBoolean(KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL, false);
sDefaults.putInt(KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT,
(int) TimeUnit.SECONDS.toMillis(30));
+ sDefaults.putString(KEY_SATELLITE_DISPLAY_NAME_STRING, "");
sDefaults.putBoolean(KEY_SATELLITE_ESOS_SUPPORTED_BOOL, false);
sDefaults.putBoolean(KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL, false);
sDefaults.putString(KEY_SATELLITE_NIDD_APN_NAME_STRING, "");
@@ -11450,7 +11512,7 @@
sDefaults.putIntArray(KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY, new int[]{1, 2, 3});
sDefaults.putInt(KEY_WEAR_CONNECTIVITY_BT_TO_CELL_DELAY_MS_INT, -1);
sDefaults.putInt(KEY_WEAR_CONNECTIVITY_EXTEND_BT_TO_CELL_DELAY_ON_WIFI_MS_INT, -1);
- sDefaults.putInt(KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE, 255);
+ sDefaults.putInt(KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE_BYTES_INT, 255);
}
/**
diff --git a/telephony/java/android/telephony/CellBroadcastService.java b/telephony/java/android/telephony/CellBroadcastService.java
index 14de2f2..60f986c 100644
--- a/telephony/java/android/telephony/CellBroadcastService.java
+++ b/telephony/java/android/telephony/CellBroadcastService.java
@@ -17,6 +17,7 @@
package android.telephony;
import android.annotation.CallSuper;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -28,6 +29,7 @@
import android.os.RemoteCallback;
import android.telephony.cdma.CdmaSmsCbProgramData;
+import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.FastPrintWriter;
import java.io.FileDescriptor;
@@ -88,9 +90,12 @@
* @param slotIndex the index of the slot which received the message
* @param bearerData the CDMA SMS bearer data
* @param serviceCategory the CDMA SCPT service category
+ * @deprecated Legacy CDMA is unsupported.
*/
- public abstract void onCdmaCellBroadcastSms(int slotIndex, @NonNull byte[] bearerData,
- @CdmaSmsCbProgramData.Category int serviceCategory);
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
+ public void onCdmaCellBroadcastSms(int slotIndex, @NonNull byte[] bearerData,
+ @CdmaSmsCbProgramData.Category int serviceCategory) {}
/**
* Handle a CDMA cell broadcast SMS message forwarded from the system.
@@ -102,10 +107,13 @@
* @param callback a callback to run after each cell broadcast receiver has handled
* the SCP message. The bundle will contain a non-separated
* dial string as and an ArrayList of {@link CdmaSmsCbProgramResults}.
+ * @deprecated Legacy CDMA is unsupported.
*/
- public abstract void onCdmaScpMessage(int slotIndex,
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
+ public void onCdmaScpMessage(int slotIndex,
@NonNull List<CdmaSmsCbProgramData> smsCbProgramData,
- @NonNull String originatingAddress, @NonNull Consumer<Bundle> callback);
+ @NonNull String originatingAddress, @NonNull Consumer<Bundle> callback) {}
/**
* Get broadcasted area information.
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 5eace54..4e5a246 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -18,11 +18,13 @@
import static android.text.TextUtils.formatSimple;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.telephony.cdma.CdmaCellLocation;
+import com.android.internal.telephony.flags.Flags;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
@@ -30,7 +32,11 @@
/**
* CellIdentity is to represent a unique CDMA cell
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+@FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+@Deprecated
public final class CellIdentityCdma extends CellIdentity {
private static final String TAG = CellIdentityCdma.class.getSimpleName();
private static final boolean DBG = false;
@@ -124,7 +130,11 @@
return new CellIdentityCdma(this);
}
- /** @hide */
+ /** @hide
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@Override
public @NonNull CellIdentityCdma sanitizeLocationInfo() {
return new CellIdentityCdma(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
@@ -157,7 +167,11 @@
/**
* @return Network Id 0..65535, {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
* if unavailable.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public int getNetworkId() {
return mNetworkId;
}
@@ -165,7 +179,11 @@
/**
* @return System Id 0..32767, {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
* if unavailable.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public int getSystemId() {
return mSystemId;
}
@@ -173,7 +191,10 @@
/**
* @return Base Station Id 0..65535, {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
* if unavailable.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public int getBasestationId() {
return mBasestationId;
}
@@ -184,7 +205,11 @@
* of 0.25 seconds and ranges from -2592000 to 2592000, both
* values inclusive (corresponding to a range of -180
* to +180 degrees). {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public int getLongitude() {
return mLongitude;
}
@@ -195,7 +220,11 @@
* of 0.25 seconds and ranges from -1296000 to 1296000, both
* values inclusive (corresponding to a range of -90
* to +90 degrees). {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public int getLatitude() {
return mLatitude;
}
@@ -206,7 +235,11 @@
super.hashCode());
}
- /** @hide */
+ /** @hide
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@NonNull
@Override
public CdmaCellLocation asCellLocation() {
@@ -277,7 +310,13 @@
if (DBG) log(toString());
}
- /** Implement the Parcelable interface */
+ /**
+ * Implement the Parcelable interface
+ *
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SuppressWarnings("hiding")
public static final @android.annotation.NonNull Creator<CellIdentityCdma> CREATOR =
new Creator<CellIdentityCdma>() {
diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java
index aa8cff5..3c4bb51 100644
--- a/telephony/java/android/telephony/CellInfoCdma.java
+++ b/telephony/java/android/telephony/CellInfoCdma.java
@@ -16,17 +16,23 @@
package android.telephony;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.telephony.flags.Flags;
import com.android.telephony.Rlog;
/**
* A {@link CellInfo} representing a CDMA cell that provides identity and measurement info.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+@FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+@Deprecated
public final class CellInfoCdma extends CellInfo implements Parcelable {
private static final String LOG_TAG = "CellInfoCdma";
@@ -61,7 +67,10 @@
/**
* @return a {@link CellIdentityCdma} instance.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@Override
public @NonNull CellIdentityCdma getCellIdentity() {
return mCellIdentityCdma;
@@ -75,7 +84,10 @@
/**
* @return a {@link CellSignalStrengthCdma} instance.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@Override
public @NonNull CellSignalStrengthCdma getCellSignalStrength() {
return mCellSignalStrengthCdma;
@@ -135,7 +147,12 @@
return 0;
}
- /** Implement the Parcelable interface */
+ /**
+ * Implement the Parcelable interface
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags, TYPE_CDMA);
@@ -154,7 +171,12 @@
if (DBG) log("CellInfoCdma(Parcel): " + toString());
}
- /** Implement the Parcelable interface */
+ /**
+ * Implement the Parcelable interface
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final @android.annotation.NonNull Creator<CellInfoCdma> CREATOR = new Creator<CellInfoCdma>() {
@Override
public CellInfoCdma createFromParcel(Parcel in) {
diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java
index d9437ab..2d650ab2 100644
--- a/telephony/java/android/telephony/PreciseDisconnectCause.java
+++ b/telephony/java/android/telephony/PreciseDisconnectCause.java
@@ -255,25 +255,75 @@
@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
public static final int EMERGENCY_PERM_FAILURE = 326;
- /** Mobile station (MS) is locked until next power cycle. */
+ /**
+ * Mobile station (MS) is locked until next power cycle.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000;
- /** Drop call. */
+ /**
+ * Drop call.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_DROP = 1001;
- /** INTERCEPT order received, Mobile station (MS) state idle entered. */
+ /**
+ * INTERCEPT order received, Mobile station (MS) state idle entered.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_INTERCEPT = 1002;
- /** Mobile station (MS) has been redirected, call is cancelled. */
+ /**
+ * Mobile station (MS) has been redirected, call is cancelled.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_REORDER = 1003;
- /** Service option rejection. */
+ /**
+ * Service option rejection.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_SO_REJECT = 1004;
- /** Requested service is rejected, retry delay is set. */
+ /**
+ * Requested service is rejected, retry delay is set.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_RETRY_ORDER = 1005;
- /** Unable to obtain access to the CDMA system. */
+ /**
+ * Unable to obtain access to the CDMA system.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_ACCESS_FAILURE = 1006;
- /** Not a preempted call. */
+ /**
+ * Not a preempted call.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_PREEMPTED = 1007;
- /** Not an emergency call. */
+ /**
+ * Not an emergency call.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_NOT_EMERGENCY = 1008;
- /** Access Blocked by CDMA network. */
+ /**
+ * Access Blocked by CDMA network.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_ACCESS_BLOCKED = 1009;
/* OEM specific error codes. To be used by OEMs when they don't want to
diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java
index 90d6f89..8b52f07 100644
--- a/telephony/java/android/telephony/RadioAccessFamily.java
+++ b/telephony/java/android/telephony/RadioAccessFamily.java
@@ -66,6 +66,9 @@
// 5G
public static final int RAF_NR = (int) TelephonyManager.NETWORK_TYPE_BITMASK_NR;
+ /** NB-IOT (Narrowband Internet of Things) over Non-Terrestrial-Networks technology. */
+ public static final int RAF_NB_IOT_NTN = (int) TelephonyManager.NETWORK_TYPE_BITMASK_NB_IOT_NTN;
+
// Grouping of RAFs
// 2G
private static final int GSM = RAF_GSM | RAF_GPRS | RAF_EDGE;
@@ -80,6 +83,9 @@
// 5G
private static final int NR = RAF_NR;
+ /** Non-Terrestrial Network. */
+ private static final int NB_IOT_NTN = RAF_NB_IOT_NTN;
+
/* Phone ID of phone */
private int mPhoneId;
@@ -258,7 +264,7 @@
raf = ((EVDO & raf) > 0) ? (EVDO | raf) : raf;
raf = ((LTE & raf) > 0) ? (LTE | raf) : raf;
raf = ((NR & raf) > 0) ? (NR | raf) : raf;
-
+ raf = ((NB_IOT_NTN & raf) > 0) ? (NB_IOT_NTN | raf) : raf;
return raf;
}
@@ -364,6 +370,7 @@
case "WCDMA": return WCDMA;
case "LTE_CA": return RAF_LTE_CA;
case "NR": return RAF_NR;
+ case "NB_IOT_NTN": return RAF_NB_IOT_NTN;
default: return RAF_UNKNOWN;
}
}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 127bbff..f8c3287 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -233,6 +233,12 @@
public static final int RIL_RADIO_TECHNOLOGY_NR = 20;
/**
+ * 3GPP NB-IOT (Narrowband Internet of Things) over Non-Terrestrial-Networks technology.
+ * @hide
+ */
+ public static final int RIL_RADIO_TECHNOLOGY_NB_IOT_NTN = 21;
+
+ /**
* RIL Radio Annotation
* @hide
*/
@@ -258,14 +264,16 @@
ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA,
ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN,
ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA,
- ServiceState.RIL_RADIO_TECHNOLOGY_NR})
+ ServiceState.RIL_RADIO_TECHNOLOGY_NR,
+ ServiceState.RIL_RADIO_TECHNOLOGY_NB_IOT_NTN
+ })
public @interface RilRadioTechnology {}
/**
* The number of the radio technologies.
*/
- private static final int NEXT_RIL_RADIO_TECHNOLOGY = 21;
+ private static final int NEXT_RIL_RADIO_TECHNOLOGY = 22;
/** @hide */
public static final int RIL_RADIO_CDMA_TECHNOLOGY_BITMASK =
@@ -1125,6 +1133,9 @@
case RIL_RADIO_TECHNOLOGY_NR:
rtString = "NR_SA";
break;
+ case RIL_RADIO_TECHNOLOGY_NB_IOT_NTN:
+ rtString = "NB_IOT_NTN";
+ break;
default:
rtString = "Unexpected";
Rlog.w(LOG_TAG, "Unexpected radioTechnology=" + rt);
@@ -1668,6 +1679,8 @@
return TelephonyManager.NETWORK_TYPE_LTE_CA;
case RIL_RADIO_TECHNOLOGY_NR:
return TelephonyManager.NETWORK_TYPE_NR;
+ case RIL_RADIO_TECHNOLOGY_NB_IOT_NTN:
+ return TelephonyManager.NETWORK_TYPE_NB_IOT_NTN;
default:
return TelephonyManager.NETWORK_TYPE_UNKNOWN;
}
@@ -1697,6 +1710,7 @@
return AccessNetworkType.CDMA2000;
case RIL_RADIO_TECHNOLOGY_LTE:
case RIL_RADIO_TECHNOLOGY_LTE_CA:
+ case RIL_RADIO_TECHNOLOGY_NB_IOT_NTN:
return AccessNetworkType.EUTRAN;
case RIL_RADIO_TECHNOLOGY_NR:
return AccessNetworkType.NGRAN;
@@ -1757,6 +1771,8 @@
return RIL_RADIO_TECHNOLOGY_LTE_CA;
case TelephonyManager.NETWORK_TYPE_NR:
return RIL_RADIO_TECHNOLOGY_NR;
+ case TelephonyManager.NETWORK_TYPE_NB_IOT_NTN:
+ return RIL_RADIO_TECHNOLOGY_NB_IOT_NTN;
default:
return RIL_RADIO_TECHNOLOGY_UNKNOWN;
}
@@ -1866,7 +1882,8 @@
|| radioTechnology == RIL_RADIO_TECHNOLOGY_TD_SCDMA
|| radioTechnology == RIL_RADIO_TECHNOLOGY_IWLAN
|| radioTechnology == RIL_RADIO_TECHNOLOGY_LTE_CA
- || radioTechnology == RIL_RADIO_TECHNOLOGY_NR;
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_NR
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_NB_IOT_NTN;
}
@@ -1886,7 +1903,8 @@
public static boolean isPsOnlyTech(int radioTechnology) {
return radioTechnology == RIL_RADIO_TECHNOLOGY_LTE
|| radioTechnology == RIL_RADIO_TECHNOLOGY_LTE_CA
- || radioTechnology == RIL_RADIO_TECHNOLOGY_NR;
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_NR
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_NB_IOT_NTN;
}
/** @hide */
diff --git a/telephony/java/android/telephony/TelephonyDisplayInfo.java b/telephony/java/android/telephony/TelephonyDisplayInfo.java
index e01b10e..bb4ce6e 100644
--- a/telephony/java/android/telephony/TelephonyDisplayInfo.java
+++ b/telephony/java/android/telephony/TelephonyDisplayInfo.java
@@ -16,12 +16,15 @@
package android.telephony;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.Annotation.NetworkType;
import android.telephony.Annotation.OverrideNetworkType;
+import com.android.internal.telephony.flags.Flags;
+
import java.util.Objects;
/**
@@ -94,6 +97,12 @@
private final boolean mIsRoaming;
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ private final boolean mIsNtn;
+
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ private final boolean mIsSatelliteConstrainedData;
+
/**
* Constructor
*
@@ -106,7 +115,7 @@
@Deprecated
public TelephonyDisplayInfo(@NetworkType int networkType,
@OverrideNetworkType int overrideNetworkType) {
- this(networkType, overrideNetworkType, false);
+ this(networkType, overrideNetworkType, false, false, false);
}
/**
@@ -118,12 +127,37 @@
*
* @hide
*/
+ @Deprecated
public TelephonyDisplayInfo(@NetworkType int networkType,
@OverrideNetworkType int overrideNetworkType,
boolean isRoaming) {
mNetworkType = networkType;
mOverrideNetworkType = overrideNetworkType;
mIsRoaming = isRoaming;
+ mIsNtn = false;
+ mIsSatelliteConstrainedData = false;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param networkType Current packet-switching cellular network type
+ * @param overrideNetworkType The override network type
+ * @param isRoaming True if the device is roaming after override.
+ * @param isNtn True if the device is camped to non-terrestrial network.
+ * @param isSatelliteConstrainedData True if the device satellite internet is bandwidth
+ * constrained.
+ *
+ * @hide
+ */
+ public TelephonyDisplayInfo(@NetworkType int networkType,
+ @OverrideNetworkType int overrideNetworkType,
+ boolean isRoaming, boolean isNtn, boolean isSatelliteConstrainedData) {
+ mNetworkType = networkType;
+ mOverrideNetworkType = overrideNetworkType;
+ mIsRoaming = isRoaming;
+ mIsNtn = isNtn;
+ mIsSatelliteConstrainedData = isSatelliteConstrainedData;
}
/** @hide */
@@ -131,6 +165,8 @@
mNetworkType = p.readInt();
mOverrideNetworkType = p.readInt();
mIsRoaming = p.readBoolean();
+ mIsNtn = p.readBoolean();
+ mIsSatelliteConstrainedData = p.readBoolean();
}
/**
@@ -170,11 +206,34 @@
return mIsRoaming;
}
+ /**
+ * Get whether the satellite internet is with bandwidth constrained capability set.
+ *
+ * @return {@code true} if satellite internet is connected with bandwidth constrained
+ * capability else {@code false}.
+ * @hide
+ */
+ public boolean isSatelliteConstrainedData() {
+ return mIsSatelliteConstrainedData;
+ }
+
+ /**
+ * Get whether the network is a non-terrestrial network.
+ *
+ * @return {@code true} if network is a non-terrestrial network else {@code false}.
+ * @hide
+ */
+ public boolean isNtn() {
+ return mIsNtn;
+ }
+
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mNetworkType);
dest.writeInt(mOverrideNetworkType);
dest.writeBoolean(mIsRoaming);
+ dest.writeBoolean(mIsNtn);
+ dest.writeBoolean(mIsSatelliteConstrainedData);
}
public static final @NonNull Parcelable.Creator<TelephonyDisplayInfo> CREATOR =
@@ -202,12 +261,15 @@
TelephonyDisplayInfo that = (TelephonyDisplayInfo) o;
return mNetworkType == that.mNetworkType
&& mOverrideNetworkType == that.mOverrideNetworkType
- && mIsRoaming == that.mIsRoaming;
+ && mIsRoaming == that.mIsRoaming
+ && mIsNtn == that.mIsNtn
+ && mIsSatelliteConstrainedData == that.mIsSatelliteConstrainedData;
}
@Override
public int hashCode() {
- return Objects.hash(mNetworkType, mOverrideNetworkType, mIsRoaming);
+ return Objects.hash(mNetworkType, mOverrideNetworkType, mIsRoaming, mIsNtn,
+ mIsSatelliteConstrainedData);
}
/**
@@ -233,6 +295,8 @@
public String toString() {
return "TelephonyDisplayInfo {network=" + TelephonyManager.getNetworkTypeName(mNetworkType)
+ ", overrideNetwork=" + overrideNetworkTypeToString(mOverrideNetworkType)
- + ", isRoaming=" + mIsRoaming + "}";
+ + ", isRoaming=" + mIsRoaming
+ + ", isNtn=" + mIsNtn
+ + ", isSatelliteConstrainedData=" + mIsSatelliteConstrainedData + "}";
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 65a52da..698821b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1397,25 +1397,44 @@
/**
* Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which leaves the roaming
* mode set to the radio default or to the user's preference if they've indicated one.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_ROAMING_MODE_RADIO_DEFAULT = -1;
/**
* Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which only permits
* connections on home networks.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_ROAMING_MODE_HOME = 0;
/**
* Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which permits roaming on
* affiliated networks.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_ROAMING_MODE_AFFILIATED = 1;
/**
* Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which permits roaming on
* any network.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CDMA_ROAMING_MODE_ANY = 2;
- /** @hide */
+ /** @hide
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @Deprecated
@IntDef(prefix = { "CDMA_ROAMING_MODE_" }, value = {
CDMA_ROAMING_MODE_RADIO_DEFAULT,
CDMA_ROAMING_MODE_HOME,
@@ -1802,12 +1821,17 @@
* to indicate if the SIM combination in DSDS has limitation or compatible issue.
* e.g. two CDMA SIMs may disrupt each other's voice call in certain scenarios.
*
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final String EXTRA_SIM_COMBINATION_WARNING_TYPE =
"android.telephony.extra.SIM_COMBINATION_WARNING_TYPE";
- /** @hide */
+ /** @hide
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @Deprecated
@IntDef({
EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE,
EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA
@@ -1818,15 +1842,21 @@
/**
* Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
* to indicate there's no SIM combination warning.
+ *
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE = 0;
/**
* Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
* to indicate two active SIMs are both CDMA hence there might be functional limitation.
+ *
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA = 1;
/**
@@ -1835,6 +1865,7 @@
* e.g. two CDMA SIMs may disrupt each other's voice call in certain scenarios, and the
* name will be "operator1 & operator2".
*
+ * TODO(b/379356026): Deprecate if this is CDMA specific
* @hide
*/
public static final String EXTRA_SIM_COMBINATION_NAMES =
@@ -2414,9 +2445,12 @@
* higher, then a SecurityException is thrown.</li>
* </ul>
*
+ * @deprecated Legacy CDMA is unsupported.
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -2456,9 +2490,12 @@
*
* @param slotIndex of which MEID is returned
*
+ * @deprecated Legacy CDMA is unsupported.
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -2485,9 +2522,12 @@
* Returns the Manufacturer Code from the MEID. Return null if Manufacturer Code is not
* available.
*
+ * @deprecated Legacy CDMA is unsupported.
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@Nullable
public String getManufacturerCode() {
@@ -2500,9 +2540,12 @@
*
* @param slotIndex of which Type Allocation Code is returned
*
+ * @deprecated Legacy CDMA is unsupported.
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@Nullable
public String getManufacturerCode(int slotIndex) {
@@ -2648,7 +2691,13 @@
public static final int PHONE_TYPE_NONE = PhoneConstants.PHONE_TYPE_NONE;
/** Phone radio is GSM. */
public static final int PHONE_TYPE_GSM = PhoneConstants.PHONE_TYPE_GSM;
- /** Phone radio is CDMA. */
+ /**
+ * Phone radio is CDMA.
+ *
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int PHONE_TYPE_CDMA = PhoneConstants.PHONE_TYPE_CDMA;
/** Phone is via SIP. */
public static final int PHONE_TYPE_SIP = PhoneConstants.PHONE_TYPE_SIP;
@@ -3070,13 +3119,33 @@
public static final int NETWORK_TYPE_EDGE = TelephonyProtoEnums.NETWORK_TYPE_EDGE; // = 2.
/** Current network is UMTS */
public static final int NETWORK_TYPE_UMTS = TelephonyProtoEnums.NETWORK_TYPE_UMTS; // = 3.
- /** Current network is CDMA: Either IS95A or IS95B*/
+ /**
+ * Current network is CDMA: Either IS95A or IS95B
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int NETWORK_TYPE_CDMA = TelephonyProtoEnums.NETWORK_TYPE_CDMA; // = 4.
- /** Current network is EVDO revision 0*/
+ /**
+ * Current network is EVDO revision 0
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int NETWORK_TYPE_EVDO_0 = TelephonyProtoEnums.NETWORK_TYPE_EVDO_0; // = 5.
- /** Current network is EVDO revision A*/
+ /**
+ * Current network is EVDO revision A
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int NETWORK_TYPE_EVDO_A = TelephonyProtoEnums.NETWORK_TYPE_EVDO_A; // = 6.
- /** Current network is 1xRTT*/
+ /**
+ * Current network is 1xRTT
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int NETWORK_TYPE_1xRTT = TelephonyProtoEnums.NETWORK_TYPE_1XRTT; // = 7.
/** Current network is HSDPA */
public static final int NETWORK_TYPE_HSDPA = TelephonyProtoEnums.NETWORK_TYPE_HSDPA; // = 8.
@@ -3090,11 +3159,21 @@
*/
@Deprecated
public static final int NETWORK_TYPE_IDEN = TelephonyProtoEnums.NETWORK_TYPE_IDEN; // = 11.
- /** Current network is EVDO revision B*/
+ /**
+ * Current network is EVDO revision B
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int NETWORK_TYPE_EVDO_B = TelephonyProtoEnums.NETWORK_TYPE_EVDO_B; // = 12.
/** Current network is LTE */
public static final int NETWORK_TYPE_LTE = TelephonyProtoEnums.NETWORK_TYPE_LTE; // = 13.
- /** Current network is eHRPD */
+ /**
+ * Current network is eHRPD
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int NETWORK_TYPE_EHRPD = TelephonyProtoEnums.NETWORK_TYPE_EHRPD; // = 14.
/** Current network is HSPA+ */
public static final int NETWORK_TYPE_HSPAP = TelephonyProtoEnums.NETWORK_TYPE_HSPAP; // = 15.
@@ -3114,6 +3193,12 @@
* For 5G NSA, the network type will be {@link #NETWORK_TYPE_LTE}.
*/
public static final int NETWORK_TYPE_NR = TelephonyProtoEnums.NETWORK_TYPE_NR; // 20.
+ /**
+ * 3GPP NB-IOT (Narrowband Internet of Things) over Non-Terrestrial-Networks technology.
+ */
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+ public static final int NETWORK_TYPE_NB_IOT_NTN =
+ TelephonyProtoEnums.NETWORK_TYPE_NB_IOT_NTN; // 21
private static final @NetworkType int[] NETWORK_TYPES = {
NETWORK_TYPE_GPRS,
@@ -3190,6 +3275,7 @@
* @see #NETWORK_TYPE_EHRPD
* @see #NETWORK_TYPE_HSPAP
* @see #NETWORK_TYPE_NR
+ * @see #NETWORK_TYPE_NB_IOT_NTN
*
* @hide
*/
@@ -3250,6 +3336,7 @@
* @see #NETWORK_TYPE_EHRPD
* @see #NETWORK_TYPE_HSPAP
* @see #NETWORK_TYPE_NR
+ * @see #NETWORK_TYPE_NB_IOT_NTN
*
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
@@ -3400,6 +3487,8 @@
return "LTE_CA";
case NETWORK_TYPE_NR:
return "NR";
+ case NETWORK_TYPE_NB_IOT_NTN:
+ return "NB_IOT_NTN";
case NETWORK_TYPE_UNKNOWN:
return "UNKNOWN";
default:
@@ -3450,6 +3539,8 @@
return NETWORK_TYPE_BITMASK_LTE;
case NETWORK_TYPE_NR:
return NETWORK_TYPE_BITMASK_NR;
+ case NETWORK_TYPE_NB_IOT_NTN:
+ return NETWORK_TYPE_BITMASK_NB_IOT_NTN;
case NETWORK_TYPE_IWLAN:
return NETWORK_TYPE_BITMASK_IWLAN;
case NETWORK_TYPE_IDEN:
@@ -6761,9 +6852,13 @@
}
}
- /** @hide */
+ /** @hide
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @Deprecated
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"ERI_"}, value = {
+ -1,
ERI_ON,
ERI_OFF,
ERI_FLASH
@@ -6773,24 +6868,37 @@
/**
* ERI (Enhanced Roaming Indicator) is ON i.e value 0 defined by
* 3GPP2 C.R1001-H v1.0 Table 8.1-1.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int ERI_ON = 0;
/**
* ERI (Enhanced Roaming Indicator) is OFF i.e value 1 defined by
* 3GPP2 C.R1001-H v1.0 Table 8.1-1.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int ERI_OFF = 1;
/**
* ERI (Enhanced Roaming Indicator) is FLASH i.e value 2 defined by
* 3GPP2 C.R1001-H v1.0 Table 8.1-1.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int ERI_FLASH = 2;
- /** @hide */
+ /** @hide
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @Deprecated
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"ERI_ICON_MODE_"}, value = {
+ -1,
ERI_ICON_MODE_NORMAL,
ERI_ICON_MODE_FLASH
})
@@ -6802,7 +6910,9 @@
*
* Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1
* @hide
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @Deprecated
public static final int ERI_ICON_MODE_NORMAL = 0;
/**
@@ -6811,7 +6921,9 @@
*
* Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1
* @hide
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @Deprecated
public static final int ERI_ICON_MODE_FLASH = 1;
/**
@@ -6819,10 +6931,13 @@
* 3GPP2 C.R1001-H v1.0 Table 8.1-1. Additionally carriers define their own ERI display numbers.
* Defined values are {@link #ERI_ON}, {@link #ERI_OFF}, and {@link #ERI_FLASH}.
*
+ * @deprecated Legacy CDMA is unsupported.
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -6832,8 +6947,10 @@
/**
* Returns the CDMA ERI icon index to display for a subscription.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@UnsupportedAppUsage
public @EriIconIndex int getCdmaEriIconIndex(int subId) {
@@ -6856,8 +6973,10 @@
* 0 - ON
* 1 - FLASHING
*
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@UnsupportedAppUsage
public @EriIconMode int getCdmaEriIconMode(int subId) {
@@ -6878,8 +6997,10 @@
/**
* Returns the CDMA ERI text,
*
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public String getCdmaEriText() {
return getCdmaEriText(getSubId());
@@ -6888,8 +7009,10 @@
/**
* Returns the CDMA ERI text, of a subscription
*
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@UnsupportedAppUsage
public String getCdmaEriText(int subId) {
@@ -8166,8 +8289,10 @@
* @param itemID the ID of the item to read.
* @return the NV item as a String, or null on any failure.
*
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
@UnsupportedAppUsage
public String nvReadItem(int itemID) {
try {
@@ -8194,8 +8319,10 @@
* @param itemValue the value to write, as a String.
* @return true on success; false on any failure.
*
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public boolean nvWriteItem(int itemID, String itemValue) {
try {
ITelephony telephony = getITelephony();
@@ -8220,8 +8347,10 @@
* @param preferredRoamingList byte array containing the new PRL.
* @return true on success; false on any failure.
*
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public boolean nvWriteCdmaPrl(byte[] preferredRoamingList) {
try {
ITelephony telephony = getITelephony();
@@ -8248,12 +8377,19 @@
* {@link #resetRadioConfig()} for reset type 3 (b/116476729)
*
* @param resetType reset type: 1: reload NV reset, 2: erase NV reset, 3: factory NV reset
+ * @deprecated NV APIs are deprecated starting from Android U.
* @return true on success; false on any failure.
*
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Deprecated
public boolean nvResetConfig(int resetType) {
+ if (Flags.cleanupCdma()) {
+ if (resetType != 1) { // 1: reload NV reset (reboot modem)
+ return false;
+ }
+ }
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
@@ -8283,14 +8419,20 @@
*
* @return {@code true} on success; {@code false} on any failure.
*
+ * @deprecated NV APIs are deprecated starting from Android U.
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
+ @Deprecated
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean resetRadioConfig() {
+ if (Flags.cleanupCdma()) {
+ return false;
+ }
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
@@ -8319,6 +8461,7 @@
* {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
+ @Deprecated
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -9272,20 +9415,26 @@
/**
* Preferred network mode is CDMA and EvDo (auto mode, according to PRL).
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int NETWORK_MODE_CDMA_EVDO = RILConstants.NETWORK_MODE_CDMA;
/**
* Preferred network mode is CDMA only.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int NETWORK_MODE_CDMA_NO_EVDO = RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
/**
* Preferred network mode is EvDo only.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int NETWORK_MODE_EVDO_NO_CDMA = RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
/**
@@ -9296,8 +9445,10 @@
/**
* Preferred network mode is LTE, CDMA and EvDo.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int NETWORK_MODE_LTE_CDMA_EVDO = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
/**
@@ -9308,8 +9459,10 @@
/**
* Preferred network mode is LTE, CDMA, EvDo, GSM/WCDMA.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA =
RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
@@ -9379,14 +9532,18 @@
/**
* Preferred network mode is TD-SCDMA,EvDo,CDMA,GSM/WCDMA.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
/**
* Preferred network mode is TD-SCDMA/LTE/GSM/WCDMA, CDMA, and EvDo.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
@@ -9404,8 +9561,10 @@
/**
* Preferred network mode is NR 5G, LTE, CDMA and EvDo.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int NETWORK_MODE_NR_LTE_CDMA_EVDO =
RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO;
@@ -9418,8 +9577,10 @@
/**
* Preferred network mode is NR 5G, LTE, CDMA, EvDo, GSM and WCDMA.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA =
RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA;
@@ -9458,8 +9619,10 @@
/**
* Preferred network mode is NR 5G, LTE, TD-SCDMA, CDMA, EVDO, GSM and WCDMA.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public static final int NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
@@ -10160,6 +10323,9 @@
* This API will result in allowing an intersection of allowed network types for all reasons,
* including the configuration done through other reasons.
*
+ * If device supports satellite service, then
+ * {@link #NETWORK_TYPE_NB_IOT_NTN} is added to allowed network types for reason by default.
+ *
* @param reason the reason the allowed network type change is taking place
* @param allowedNetworkTypes The bitmask of allowed network type
* @throws IllegalStateException if the Telephony process is not currently available.
@@ -10209,6 +10375,10 @@
* <p>Requires permission: android.Manifest.READ_PRIVILEGED_PHONE_STATE or
* that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
+ * If device supports satellite service, then
+ * {@link #NETWORK_TYPE_NB_IOT_NTN} is added to allowed network types for reason by
+ * default.
+ *
* @param reason the reason the allowed network type change is taking place
* @return the allowed network type bitmask
* @throws IllegalStateException if the Telephony process is not currently available.
@@ -10275,7 +10445,7 @@
*/
public static String convertNetworkTypeBitmaskToString(
@NetworkTypeBitMask long networkTypeBitmask) {
- String networkTypeName = IntStream.rangeClosed(NETWORK_TYPE_GPRS, NETWORK_TYPE_NR)
+ String networkTypeName = IntStream.rangeClosed(NETWORK_TYPE_GPRS, NETWORK_TYPE_NB_IOT_NTN)
.filter(x -> {
return (networkTypeBitmask & getBitMaskForNetworkType(x))
== getBitMaskForNetworkType(x);
@@ -10533,8 +10703,11 @@
/**
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -10545,8 +10718,11 @@
/**
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -10566,8 +10742,11 @@
/**
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -10578,8 +10757,11 @@
/**
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -11868,8 +12050,11 @@
*
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -11912,8 +12097,11 @@
*
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -11935,7 +12123,10 @@
}
}
- /** @hide */
+ /** @hide
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @Deprecated
@IntDef(prefix = { "CDMA_SUBSCRIPTION_" }, value = {
CDMA_SUBSCRIPTION_UNKNOWN,
CDMA_SUBSCRIPTION_RUIM_SIM,
@@ -11946,22 +12137,31 @@
/**
* Used for CDMA subscription mode, it'll be UNKNOWN if there is no Subscription source.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1;
/**
* Used for CDMA subscription mode: RUIM/SIM (default)
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0;
/**
* Used for CDMA subscription mode: NV -> non-volatile memory
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
public static final int CDMA_SUBSCRIPTION_NV = 1;
@@ -11982,8 +12182,11 @@
*
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -12022,8 +12225,11 @@
*
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -13747,8 +13953,11 @@
*
* @throws UnsupportedOperationException If the device does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public String getCdmaPrlVersion() {
@@ -13762,8 +13971,10 @@
*
* @param subId the subscription ID that this request applies to.
* @return PRLVersion or null if error.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @Deprecated
public String getCdmaPrlVersion(int subId) {
try {
ITelephony service = getITelephony();
@@ -14905,7 +15116,8 @@
NETWORK_TYPE_BITMASK_LTE_CA,
NETWORK_TYPE_BITMASK_NR,
NETWORK_TYPE_BITMASK_IWLAN,
- NETWORK_TYPE_BITMASK_IDEN
+ NETWORK_TYPE_BITMASK_IDEN,
+ NETWORK_TYPE_BITMASK_NB_IOT_NTN
})
public @interface NetworkTypeBitMask {}
@@ -14928,7 +15140,10 @@
public static final long NETWORK_TYPE_BITMASK_EDGE = (1 << (NETWORK_TYPE_EDGE -1));
/**
* network type bitmask indicating the support of radio tech CDMA(IS95A/IS95B).
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final long NETWORK_TYPE_BITMASK_CDMA = (1 << (NETWORK_TYPE_CDMA -1));
/**
* network type bitmask indicating the support of radio tech 1xRTT.
@@ -14950,7 +15165,10 @@
public static final long NETWORK_TYPE_BITMASK_EVDO_B = (1 << (NETWORK_TYPE_EVDO_B -1));
/**
* network type bitmask indicating the support of radio tech EHRPD.
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final long NETWORK_TYPE_BITMASK_EHRPD = (1 << (NETWORK_TYPE_EHRPD -1));
/**
* network type bitmask indicating the support of radio tech HSUPA.
@@ -15006,6 +15224,12 @@
*/
public static final long NETWORK_TYPE_BITMASK_IWLAN = (1 << (NETWORK_TYPE_IWLAN -1));
+ /**
+ * network type bitmask indicating the support of readio tech NB IOT NTN.
+ */
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+ public static final long NETWORK_TYPE_BITMASK_NB_IOT_NTN = (1 << (NETWORK_TYPE_NB_IOT_NTN - 1));
+
/** @hide */
public static final long NETWORK_CLASS_BITMASK_2G = NETWORK_TYPE_BITMASK_GSM
| NETWORK_TYPE_BITMASK_GPRS
@@ -15034,6 +15258,9 @@
public static final long NETWORK_CLASS_BITMASK_5G = NETWORK_TYPE_BITMASK_NR;
/** @hide */
+ public static final long NETWORK_CLASS_BITMASK_NTN = NETWORK_TYPE_BITMASK_NB_IOT_NTN;
+
+ /** @hide */
public static final long NETWORK_STANDARDS_FAMILY_BITMASK_3GPP = NETWORK_TYPE_BITMASK_GSM
| NETWORK_TYPE_BITMASK_GPRS
| NETWORK_TYPE_BITMASK_EDGE
@@ -15045,9 +15272,13 @@
| NETWORK_TYPE_BITMASK_TD_SCDMA
| NETWORK_TYPE_BITMASK_LTE
| NETWORK_TYPE_BITMASK_LTE_CA
- | NETWORK_TYPE_BITMASK_NR;
+ | NETWORK_TYPE_BITMASK_NR
+ | NETWORK_TYPE_BITMASK_NB_IOT_NTN;
- /** @hide */
+ /** @hide
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @Deprecated
public static final long NETWORK_STANDARDS_FAMILY_BITMASK_3GPP2 = NETWORK_TYPE_BITMASK_CDMA
| NETWORK_TYPE_BITMASK_1xRTT
| NETWORK_TYPE_BITMASK_EVDO_0
@@ -18083,7 +18314,7 @@
*/
public static boolean isNetworkTypeValid(@NetworkType int networkType) {
return networkType >= TelephonyManager.NETWORK_TYPE_UNKNOWN &&
- networkType <= TelephonyManager.NETWORK_TYPE_NR;
+ networkType <= TelephonyManager.NETWORK_TYPE_NB_IOT_NTN;
}
/**
diff --git a/telephony/java/android/telephony/cdma/CdmaSmsCbProgramData.java b/telephony/java/android/telephony/cdma/CdmaSmsCbProgramData.java
index 02429b5..8fccf65 100644
--- a/telephony/java/android/telephony/cdma/CdmaSmsCbProgramData.java
+++ b/telephony/java/android/telephony/cdma/CdmaSmsCbProgramData.java
@@ -16,12 +16,15 @@
package android.telephony.cdma;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.telephony.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -34,18 +37,36 @@
* containing an array of these objects to update its list of cell broadcast service categories
* to display.
*
+ * @deprecated Legacy CDMA is unsupported.
* {@hide}
*/
+@FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+@Deprecated
@SystemApi
public final class CdmaSmsCbProgramData implements Parcelable {
- /** Delete the specified service category from the list of enabled categories. */
+ /**
+ * Delete the specified service category from the list of enabled categories.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int OPERATION_DELETE_CATEGORY = 0;
- /** Add the specified service category to the list of enabled categories. */
+ /**
+ * Add the specified service category to the list of enabled categories.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int OPERATION_ADD_CATEGORY = 1;
- /** Clear all service categories from the list of enabled categories. */
+ /**
+ * Clear all service categories from the list of enabled categories.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int OPERATION_CLEAR_CATEGORIES = 2;
/** @hide */
@@ -59,23 +80,53 @@
public @interface Operation {}
// CMAS alert service category assignments, see 3GPP2 C.R1001 table 9.3.3-1
- /** Indicates a presidential-level alert */
+ /**
+ * Indicates a presidential-level alert
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT = 0x1000;
- /** Indicates an extreme threat to life and property */
+ /**
+ * Indicates an extreme threat to life and property
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CATEGORY_CMAS_EXTREME_THREAT = 0x1001;
- /** Indicates an severe threat to life and property */
+ /**
+ * Indicates an severe threat to life and property
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CATEGORY_CMAS_SEVERE_THREAT = 0x1002;
- /** Indicates an AMBER child abduction emergency */
+ /**
+ * Indicates an AMBER child abduction emergency
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY = 0x1003;
- /** Indicates a CMAS test message */
+ /**
+ * Indicates a CMAS test message
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CATEGORY_CMAS_TEST_MESSAGE = 0x1004;
- /** The last reserved value of a CMAS service category according to 3GPP C.R1001 table
- * 9.3.3-1. */
+ /**
+ * The last reserved value of a CMAS service category according to 3GPP C.R1001 table
+ * 9.3.3-1.
+ * @deprecated Legacy CDMA is unsupported.
+ */
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public static final int CATEGORY_CMAS_LAST_RESERVED_VALUE = 0x10ff;
/** @hide */
@@ -177,7 +228,10 @@
*
* @param dest The Parcel in which the object should be written.
* @param flags Additional flags about how the object should be written (ignored).
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mOperation);
@@ -192,7 +246,10 @@
* Returns the service category operation, e.g. {@link #OPERATION_ADD_CATEGORY}.
*
* @return the service category operation
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public @Operation int getOperation() {
return mOperation;
}
@@ -203,7 +260,10 @@
* 0x10FF are supported.
*
* @return a 16-bit CDMA service category value
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
public @Category int getCategory() {
return mCategory;
}
@@ -255,7 +315,11 @@
/**
* Describe the kinds of special objects contained in the marshalled representation.
* @return a bitmask indicating this Parcelable contains no special objects
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@Override
public int describeContents() {
return 0;
@@ -263,7 +327,11 @@
/**
* Creator for unparcelling objects.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@NonNull
public static final Parcelable.Creator<CdmaSmsCbProgramData>
CREATOR = new Parcelable.Creator<CdmaSmsCbProgramData>() {
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 8925a9e..76f83ee 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -18,6 +18,7 @@
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -37,6 +38,8 @@
import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
import android.util.Log;
+import com.android.internal.telephony.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -226,8 +229,11 @@
/**
* A capability update has been requested due to moving to eHRPD.
+ * @deprecated Legacy CDMA is unsupported.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
+ @Deprecated
@SystemApi
public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 4;
diff --git a/telephony/java/android/telephony/satellite/SatelliteDisallowedReasonsCallback.java b/telephony/java/android/telephony/satellite/SatelliteDisallowedReasonsCallback.java
index 5e276aa..0a1eedf 100644
--- a/telephony/java/android/telephony/satellite/SatelliteDisallowedReasonsCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteDisallowedReasonsCallback.java
@@ -18,6 +18,7 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import com.android.internal.telephony.flags.Flags;
@@ -26,13 +27,14 @@
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public interface SatelliteDisallowedReasonsCallback {
/**
* Called when disallowed reason of satellite has changed.
* @param disallowedReasons Integer array of disallowed reasons.
*/
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
- void onSatelliteDisallowedReasonsChanged(@NonNull int[] disallowedReasons);
+ void onSatelliteDisallowedReasonsChanged(
+ @NonNull @SatelliteManager.SatelliteDisallowedReason int[] disallowedReasons);
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index db5689b7..0f23f33 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -292,6 +292,13 @@
/**
* Bundle key to get the response from
+ * {@link #requestSatelliteDisplayName(Executor, OutcomeReceiver)}.
+ * @hide
+ */
+ public static final String KEY_SATELLITE_DISPLAY_NAME = "satellite_display_name";
+
+ /**
+ * Bundle key to get the response from
* {@link #requestSelectedNbIotSatelliteSubscriptionId(Executor, OutcomeReceiver)}.
* @hide
*/
@@ -553,6 +560,8 @@
* There is no valid satellite subscription selected.
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int SATELLITE_RESULT_NO_VALID_SATELLITE_SUBSCRIPTION = 30;
/** @hide */
@@ -2386,8 +2395,9 @@
*
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public void requestSatelliteAccessConfigurationForCurrentLocation(
@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<SatelliteAccessConfiguration, SatelliteException> callback) {
@@ -2500,7 +2510,7 @@
* @param executor The executor on which the callback will be called.
* @param callback The callback object to which the result will be delivered.
* If the request is successful, {@link OutcomeReceiver#onResult(Object)}
- * will return the time after which the satellite will be visible.
+ * will return the selected NB IOT satellite subscription ID.
* If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
* will return a {@link SatelliteException} with the {@link SatelliteResult}.
*
@@ -2508,6 +2518,8 @@
*
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
public void requestSelectedNbIotSatelliteSubscriptionId(
@NonNull @CallbackExecutor Executor executor,
@@ -2567,6 +2579,8 @@
*
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
@SatelliteResult public int registerForSelectedNbIotSatelliteSubscriptionChanged(
@NonNull @CallbackExecutor Executor executor,
@@ -2612,6 +2626,8 @@
* @throws IllegalStateException if the Telephony process is not currently available.
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
public void unregisterForSelectedNbIotSatelliteSubscriptionChanged(
@NonNull SelectedNbIotSatelliteSubscriptionCallback callback) {
@@ -2890,27 +2906,22 @@
/**
* Returns list of disallowed reasons of satellite.
*
- * @return list of disallowed reasons of satellite.
+ * @return Integer array of disallowed reasons.
*
* @throws SecurityException if caller doesn't have required permission.
* @throws IllegalStateException if Telephony process isn't available.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
@SatelliteDisallowedReason
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
@NonNull
- public List<Integer> getSatelliteDisallowedReasons() {
+ public int[] getSatelliteDisallowedReasons() {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- int[] receivedArray = telephony.getSatelliteDisallowedReasons();
- if (receivedArray.length == 0) {
- logd("receivedArray is empty, create empty list");
- return new ArrayList<>();
- } else {
- return Arrays.stream(receivedArray).boxed().collect(Collectors.toList());
- }
+ return telephony.getSatelliteDisallowedReasons();
} else {
throw new IllegalStateException("Telephony service is null.");
}
@@ -2918,7 +2929,7 @@
loge("getSatelliteDisallowedReasons() RemoteException: " + ex);
ex.rethrowAsRuntimeException();
}
- return new ArrayList<>();
+ return new int[0];
}
/**
@@ -2931,8 +2942,9 @@
* @throws IllegalStateException if Telephony process is not available.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public void registerForSatelliteDisallowedReasonsChanged(
@NonNull @CallbackExecutor Executor executor,
@NonNull SatelliteDisallowedReasonsCallback callback) {
@@ -2974,8 +2986,9 @@
* @throws IllegalStateException if Telephony process is not available.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public void unregisterForSatelliteDisallowedReasonsChanged(
@NonNull SatelliteDisallowedReasonsCallback callback) {
Objects.requireNonNull(callback);
@@ -3594,6 +3607,67 @@
}
/**
+ * Request to get the display name of satellite feature in the UI.
+ *
+ * @param executor The executor on which the callback will be called.
+ * @param callback The callback object to which the result will be delivered.
+ * If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+ * will return display name of the satellite feature in string format. Default
+ * display name is "Satellite". If the request is not successful,
+ * {@link OutcomeReceiver#onError(Throwable)} will return an error with
+ * a SatelliteException.
+ *
+ * @throws SecurityException if the caller doesn't have required permission.
+ * @throws IllegalStateException if the Telephony process is not currently available.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ public void requestSatelliteDisplayName(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OutcomeReceiver<CharSequence, SatelliteException> callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ ResultReceiver receiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (resultCode == SATELLITE_RESULT_SUCCESS) {
+ if (resultData.containsKey(KEY_SATELLITE_DISPLAY_NAME)) {
+ CharSequence satelliteDisplayName =
+ resultData.getString(KEY_SATELLITE_DISPLAY_NAME);
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onResult(satelliteDisplayName)));
+ } else {
+ loge("KEY_SATELLITE_DISPLAY_NAME does not exist.");
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onError(new SatelliteException(
+ SATELLITE_RESULT_REQUEST_FAILED))));
+ }
+ } else {
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onError(new SatelliteException(resultCode))));
+ }
+ }
+ };
+ telephony.requestSatelliteDisplayName(receiver);
+ } else {
+ loge("requestSatelliteDisplayName() invalid telephony");
+ executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+ new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+ }
+ } catch (RemoteException ex) {
+ loge("requestSatelliteDisplayName() RemoteException: " + ex);
+ executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+ new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+ }
+ }
+
+ /**
* Deliver the list of provisioned satellite subscriber infos.
*
* @param list The list of provisioned satellite subscriber infos.
diff --git a/telephony/java/android/telephony/satellite/SelectedNbIotSatelliteSubscriptionCallback.java b/telephony/java/android/telephony/satellite/SelectedNbIotSatelliteSubscriptionCallback.java
index d896554..76caef3 100644
--- a/telephony/java/android/telephony/satellite/SelectedNbIotSatelliteSubscriptionCallback.java
+++ b/telephony/java/android/telephony/satellite/SelectedNbIotSatelliteSubscriptionCallback.java
@@ -16,11 +16,18 @@
package android.telephony.satellite;
+import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
+
+import com.android.internal.telephony.flags.Flags;
+
/**
* A callback class for selected satellite subscription changed events.
*
* @hide
*/
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public interface SelectedNbIotSatelliteSubscriptionCallback {
/**
* Called when the selected satellite subscription has changed.
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 131f46b..74d9204 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -416,6 +416,8 @@
* Returns the CDMA ERI icon index to display
* @param callingPackage package making the call.
* @param callingFeatureId The feature in the package.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
int getCdmaEriIconIndex(String callingPackage, String callingFeatureId);
@@ -424,6 +426,8 @@
* @param subId user preferred subId.
* @param callingPackage package making the call.
* @param callingFeatureId The feature in the package.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
int getCdmaEriIconIndexForSubscriber(int subId, String callingPackage,
String callingFeatureId);
@@ -434,6 +438,8 @@
* 1 - FLASHING
* @param callingPackage package making the call.
* @param callingFeatureId The feature in the package.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
int getCdmaEriIconMode(String callingPackage, String callingFeatureId);
@@ -444,6 +450,8 @@
* @param subId user preferred subId.
* @param callingPackage package making the call.
* @param callingFeatureId The feature in the package.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
int getCdmaEriIconModeForSubscriber(int subId, String callingPackage,
String callingFeatureId);
@@ -452,6 +460,8 @@
* Returns the CDMA ERI text,
* @param callingPackage package making the call.
* @param callingFeatureId The feature in the package.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
String getCdmaEriText(String callingPackage, String callingFeatureId);
@@ -460,6 +470,8 @@
* @param subId user preferred subId.
* @param callingPackage package making the call.
* @param callingFeatureId The feature in the package.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
String getCdmaEriTextForSubscriber(int subId, String callingPackage, String callingFeatureId);
@@ -779,6 +791,8 @@
*
* @param itemID the ID of the item to read.
* @return the NV item as a String, or null on any failure.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
String nvReadItem(int itemID);
@@ -789,6 +803,8 @@
* @param itemID the ID of the item to read.
* @param itemValue the value to write, as a String.
* @return true on success; false on any failure.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
boolean nvWriteItem(int itemID, String itemValue);
@@ -798,6 +814,8 @@
*
* @param preferredRoamingList byte array containing the new PRL.
* @return true on success; false on any failure.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
boolean nvWriteCdmaPrl(in byte[] preferredRoamingList);
@@ -811,6 +829,8 @@
*
* @param slotIndex - device slot.
* @return {@code true} on success; {@code false} on any failure.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
boolean resetModemConfig(int slotIndex);
@@ -1041,12 +1061,16 @@
/**
* Return MDN string for CDMA phone.
* @param subId user preferred subId.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
String getCdmaMdn(int subId);
/**
* Return MIN string for CDMA phone.
* @param subId user preferred subId.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
String getCdmaMin(int subId);
@@ -1495,13 +1519,14 @@
String getEsn(int subId);
/**
- * Return the Preferred Roaming List Version
- *
- * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
- * @param subId the subscription ID that this request applies to.
- * @return PRLVersion or null if error.
- * @hide
- */
+ * Return the Preferred Roaming List Version
+ *
+ * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+ * @param subId the subscription ID that this request applies to.
+ * @return PRLVersion or null if error.
+ * @hide
+ * @deprecated Legacy CDMA is unsupported.
+ */
String getCdmaPrlVersion(int subId);
/**
@@ -1804,6 +1829,8 @@
*
* @param the subscription id.
* @return the roaming mode for CDMA phone.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
int getCdmaRoamingMode(int subId);
@@ -1814,6 +1841,8 @@
* @param subId the subscription id.
* @param mode the roaming mode should be set.
* @return {@code true} if successed.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
boolean setCdmaRoamingMode(int subId, int mode);
@@ -1822,6 +1851,8 @@
*
* @param the subscription id.
* @return the subscription mode for CDMA phone.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
int getCdmaSubscriptionMode(int subId);
@@ -1832,6 +1863,8 @@
* @param subId the subscription id.
* @param mode the subscription mode should be set.
* @return {@code true} if successed.
+ *
+ * @deprecated Legacy CDMA is unsupported.
*/
boolean setCdmaSubscriptionMode(int subId, int mode);
@@ -3471,6 +3504,17 @@
void requestSatelliteSubscriberProvisionStatus(in ResultReceiver result);
/**
+ * Request to get the name to display for Satellite subscription.
+ *
+ * @param receiver The result receiver that returns the diplay name to be used for the satellite
+ * subscription.
+ * @hide
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+ void requestSatelliteDisplayName(in ResultReceiver receiver);
+
+ /**
* Deliver the list of provisioned satellite subscriber infos.
*
* @param list The list of provisioned satellite subscriber infos.
diff --git a/tests/Input/res/xml/bookmarks.xml b/tests/Input/res/xml/bookmarks.xml
index a4c898d..68ec123 100644
--- a/tests/Input/res/xml/bookmarks.xml
+++ b/tests/Input/res/xml/bookmarks.xml
@@ -23,7 +23,7 @@
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CONTACTS"
- androidprv:keycode="KEYCODE_C"
+ androidprv:keycode="KEYCODE_P"
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_EMAIL"
@@ -31,21 +31,13 @@
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CALENDAR"
- androidprv:keycode="KEYCODE_K"
+ androidprv:keycode="KEYCODE_C"
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_MAPS"
androidprv:keycode="KEYCODE_M"
androidprv:modifierState="META" />
<bookmark
- category="android.intent.category.APP_MUSIC"
- androidprv:keycode="KEYCODE_P"
- androidprv:modifierState="META" />
- <bookmark
- role="android.app.role.SMS"
- androidprv:keycode="KEYCODE_S"
- androidprv:modifierState="META" />
- <bookmark
category="android.intent.category.APP_CALCULATOR"
androidprv:keycode="KEYCODE_U"
androidprv:modifierState="META" />
@@ -57,7 +49,7 @@
<bookmark
category="android.intent.category.APP_CONTACTS"
- androidprv:keycode="KEYCODE_C"
+ androidprv:keycode="KEYCODE_P"
shift="true" />
<bookmark
@@ -65,4 +57,4 @@
class="com.test.BookmarkTest"
androidprv:keycode="KEYCODE_J"
shift="true" />
-</bookmarks>
\ No newline at end of file
+</bookmarks>
diff --git a/tests/Input/res/xml/bookmarks_legacy.xml b/tests/Input/res/xml/bookmarks_legacy.xml
index 8bacf49..78cc48b 100644
--- a/tests/Input/res/xml/bookmarks_legacy.xml
+++ b/tests/Input/res/xml/bookmarks_legacy.xml
@@ -22,23 +22,17 @@
shortcut="b" />
<bookmark
category="android.intent.category.APP_CONTACTS"
- shortcut="c" />
+ shortcut="p" />
<bookmark
category="android.intent.category.APP_EMAIL"
shortcut="e" />
<bookmark
category="android.intent.category.APP_CALENDAR"
- shortcut="k" />
+ shortcut="c" />
<bookmark
category="android.intent.category.APP_MAPS"
shortcut="m" />
<bookmark
- category="android.intent.category.APP_MUSIC"
- shortcut="p" />
- <bookmark
- role="android.app.role.SMS"
- shortcut="s" />
- <bookmark
category="android.intent.category.APP_CALCULATOR"
shortcut="u" />
@@ -49,7 +43,7 @@
<bookmark
category="android.intent.category.APP_CONTACTS"
- shortcut="c"
+ shortcut="p"
shift="true" />
<bookmark
@@ -57,4 +51,4 @@
class="com.test.BookmarkTest"
shortcut="j"
shift="true" />
-</bookmarks>
\ No newline at end of file
+</bookmarks>
diff --git a/tests/Input/src/com/android/server/input/InputDataStoreTests.kt b/tests/Input/src/com/android/server/input/InputDataStoreTests.kt
new file mode 100644
index 0000000..78c828baf
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/InputDataStoreTests.kt
@@ -0,0 +1,504 @@
+/*
+ * 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.
+ */
+
+package com.android.server.input
+
+import android.app.role.RoleManager
+import android.content.Context
+import android.content.ContextWrapper
+import android.content.Intent
+import android.hardware.input.AppLaunchData
+import android.hardware.input.InputGestureData
+import android.hardware.input.KeyGestureEvent
+import android.platform.test.annotations.Presubmit
+import android.util.AtomicFile
+import android.view.KeyEvent
+import androidx.test.core.app.ApplicationProvider
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.io.FileOutputStream
+import java.io.InputStream
+import java.nio.charset.StandardCharsets
+import kotlin.test.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito
+
+/**
+ * Tests for {@link InputDataStore}.
+ *
+ * Build/Install/Run:
+ * atest InputTests:InputDataStoreTests
+ */
+@Presubmit
+class InputDataStoreTests {
+
+ companion object {
+ const val USER_ID = 1
+ }
+
+ private lateinit var context: Context
+ private lateinit var inputDataStore: InputDataStore
+ private lateinit var tempFile: File
+
+ @Before
+ fun setup() {
+ context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
+ setupInputDataStore()
+ }
+
+ private fun setupInputDataStore() {
+ tempFile = File.createTempFile("input_gestures", ".xml")
+ inputDataStore = InputDataStore(object : InputDataStore.FileInjector("input_gestures") {
+ private val atomicFile: AtomicFile = AtomicFile(tempFile)
+
+ override fun openRead(userId: Int): InputStream? {
+ return atomicFile.openRead()
+ }
+
+ override fun startWrite(userId: Int): FileOutputStream? {
+ return atomicFile.startWrite()
+ }
+
+ override fun finishWrite(userId: Int, fos: FileOutputStream?, success: Boolean) {
+ if (success) {
+ atomicFile.finishWrite(fos)
+ } else {
+ atomicFile.failWrite(fos)
+ }
+ }
+ })
+ }
+
+ private fun getPrintableXml(inputGestures: List<InputGestureData>): String {
+ val outputStream = ByteArrayOutputStream()
+ inputDataStore.writeInputGestureXml(outputStream, true, inputGestures)
+ return outputStream.toString(StandardCharsets.UTF_8).trimIndent()
+ }
+
+ @Test
+ fun saveToDiskKeyGesturesOnly() {
+ val inputGestures = listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_H,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_1,
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_2,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
+ .build()
+ )
+
+ inputDataStore.saveInputGestures(USER_ID, inputGestures)
+ assertEquals(
+ inputGestures,
+ inputDataStore.loadInputGestures(USER_ID),
+ getPrintableXml(inputGestures)
+ )
+ }
+
+ @Test
+ fun saveToDiskTouchpadGestures() {
+ val inputGestures = listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createTouchpadTrigger(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build()
+ )
+
+ inputDataStore.saveInputGestures(USER_ID, inputGestures)
+ assertEquals(
+ inputGestures,
+ inputDataStore.loadInputGestures(USER_ID),
+ getPrintableXml(inputGestures)
+ )
+ }
+
+ @Test
+ fun saveToDiskAppLaunchGestures() {
+ val inputGestures = listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createTouchpadTrigger(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ .setAppLaunchData(AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER))
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_2,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ .setAppLaunchData(AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS))
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_1,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ .setAppLaunchData(
+ AppLaunchData.createLaunchDataForComponent(
+ "com.test",
+ "com.test.BookmarkTest"
+ )
+ )
+ .build()
+ )
+
+ inputDataStore.saveInputGestures(USER_ID, inputGestures)
+ assertEquals(
+ inputGestures,
+ inputDataStore.loadInputGestures(USER_ID),
+ getPrintableXml(inputGestures)
+ )
+ }
+
+ @Test
+ fun saveToDiskCombinedGestures() {
+ val inputGestures = listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_1,
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_2,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createTouchpadTrigger(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_9,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ .setAppLaunchData(AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS))
+ .build(),
+ )
+
+ inputDataStore.saveInputGestures(USER_ID, inputGestures)
+ assertEquals(
+ inputGestures,
+ inputDataStore.loadInputGestures(USER_ID),
+ getPrintableXml(inputGestures)
+ )
+ }
+
+ @Test
+ fun validXmlParse() {
+ val xmlData = """
+ <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+ <root>
+ <input_gesture_list>
+ <input_gesture key_gesture_type="3">
+ <key_trigger keycode="8" modifiers="69632" />
+ </input_gesture>
+ <input_gesture key_gesture_type="21">
+ <key_trigger keycode="9" modifiers="65536" />
+ </input_gesture>
+ <input_gesture key_gesture_type="1">
+ <touchpad_trigger touchpad_gesture_type="1" />
+ </input_gesture>
+ </input_gesture_list>
+ </root>""".trimIndent()
+ val validInputGestures = listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_1,
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_2,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createTouchpadTrigger(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build()
+ )
+ val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
+ assertEquals(
+ validInputGestures,
+ inputDataStore.readInputGesturesXml(inputStream, true)
+ )
+ }
+
+ @Test
+ fun missingTriggerData() {
+ val xmlData = """
+ <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+ <root>
+ <input_gesture_list>
+ <input_gesture key_gesture_type="3">
+ </input_gesture>
+ <input_gesture key_gesture_type="21">
+ <key_trigger keycode="9" modifiers="65536" />
+ </input_gesture>
+ <input_gesture key_gesture_type="1">
+ <touchpad_trigger touchpad_gesture_type="1" />
+ </input_gesture>
+ </input_gesture_list>
+ </root>""".trimIndent()
+ val validInputGestures = listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_2,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createTouchpadTrigger(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build()
+ )
+ val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
+ assertEquals(
+ validInputGestures,
+ inputDataStore.readInputGesturesXml(inputStream, true)
+ )
+ }
+
+ @Test
+ fun invalidKeycode() {
+ val xmlData = """
+ <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+ <root>
+ <input_gesture_list>
+ <input_gesture key_gesture_type="3">
+ <key_trigger keycode="8" modifiers="69632" />
+ </input_gesture>
+ <input_gesture key_gesture_type="21">
+ <key_trigger keycode="9999999" modifiers="65536" />
+ </input_gesture>
+ <input_gesture key_gesture_type="1">
+ <touchpad_trigger touchpad_gesture_type="1" />
+ </input_gesture>
+ </input_gesture_list>
+ </root>""".trimIndent()
+ val validInputGestures = listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_1,
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createTouchpadTrigger(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build()
+ )
+ val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
+ assertEquals(
+ validInputGestures,
+ inputDataStore.readInputGesturesXml(inputStream, true)
+ )
+ }
+
+ @Test
+ fun invalidTriggerName() {
+ val xmlData = """
+ <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+ <root>
+ <input_gesture_list>
+ <input_gesture key_gesture_type="3">
+ <key_trigger keycode="8" modifiers="69632" />
+ </input_gesture>
+ <input_gesture key_gesture_type="21">
+ <key_trigger keycode="9" modifiers="65536" />
+ </input_gesture>
+ <input_gesture key_gesture_type="1">
+ <invalid_trigger_name touchpad_gesture_type="1" />
+ </input_gesture>
+ </input_gesture_list>
+ </root>""".trimIndent()
+ val validInputGestures = listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_1,
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_2,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
+ .build(),
+ )
+ val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
+ assertEquals(
+ validInputGestures,
+ inputDataStore.readInputGesturesXml(inputStream, true)
+ )
+ }
+
+ @Test
+ fun invalidTouchpadGestureType() {
+ val xmlData = """
+ <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+ <root>
+ <input_gesture_list>
+ <input_gesture key_gesture_type="3">
+ <key_trigger keycode="8" modifiers="69632" />
+ </input_gesture>
+ <input_gesture key_gesture_type="21">
+ <key_trigger keycode="9" modifiers="65536" />
+ </input_gesture>
+ <input_gesture key_gesture_type="1">
+ <touchpad_trigger touchpad_gesture_type="9999" />
+ </input_gesture>
+ </input_gesture_list>
+ </root>""".trimIndent()
+ val validInputGestures = listOf(
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_1,
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build(),
+ InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_2,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS)
+ .build(),
+ )
+ val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
+ assertEquals(
+ validInputGestures,
+ inputDataStore.readInputGesturesXml(inputStream, true)
+ )
+ }
+
+ @Test
+ fun emptyInputGestureList() {
+ val xmlData = """
+ <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+ <root>
+ <input_gesture_list>
+ </input_gesture_list>
+ </root>""".trimIndent()
+ val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
+ assertEquals(
+ listOf(),
+ inputDataStore.readInputGesturesXml(inputStream, true)
+ )
+ }
+
+ @Test
+ fun invalidTag() {
+ val xmlData = """
+ <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+ <root>
+ <invalid_tag_name>
+ </invalid_tag_name>
+ </root>""".trimIndent()
+ val inputStream = ByteArrayInputStream(xmlData.toByteArray(Charsets.UTF_8))
+ assertEquals(
+ listOf(),
+ inputDataStore.readInputGesturesXml(inputStream, true)
+ )
+ }
+}
\ No newline at end of file
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index 43844f6..038c6d7 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -541,7 +541,8 @@
0
},
"title",
- /* uid = */0
+ /* uid = */0,
+ /* inputFeatureFlags = */ 0
)
whenever(windowManagerInternal.getKeyInterceptionInfoFromToken(any())).thenReturn(info)
}
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index 36a89f9..fafb0e0 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -40,6 +40,7 @@
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.Presubmit
import android.platform.test.flag.junit.SetFlagsRule
+import android.util.AtomicFile
import android.view.InputDevice
import android.view.KeyCharacterMap
import android.view.KeyEvent
@@ -50,6 +51,9 @@
import com.android.internal.annotations.Keep
import com.android.internal.util.FrameworkStatsLog
import com.android.modules.utils.testing.ExtendedMockitoRule
+import java.io.File
+import java.io.FileOutputStream
+import java.io.InputStream
import junitparams.JUnitParamsRunner
import junitparams.Parameters
import org.junit.After
@@ -123,6 +127,8 @@
private lateinit var keyGestureController: KeyGestureController
private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
private lateinit var testLooper: TestLooper
+ private lateinit var tempFile: File
+ private lateinit var inputDataStore: InputDataStore
private var events = mutableListOf<KeyGestureEvent>()
@Before
@@ -133,6 +139,31 @@
setupBehaviors()
testLooper = TestLooper()
currentPid = Process.myPid()
+ tempFile = File.createTempFile("input_gestures", ".xml")
+ inputDataStore =
+ InputDataStore(object : InputDataStore.FileInjector("input_gestures.xml") {
+ private val atomicFile: AtomicFile = AtomicFile(tempFile)
+
+ override fun openRead(userId: Int): InputStream? {
+ return atomicFile.openRead()
+ }
+
+ override fun startWrite(userId: Int): FileOutputStream? {
+ return atomicFile.startWrite()
+ }
+
+ override fun finishWrite(userId: Int, fos: FileOutputStream?, success: Boolean) {
+ if (success) {
+ atomicFile.finishWrite(fos)
+ } else {
+ atomicFile.failWrite(fos)
+ }
+ }
+
+ override fun getAtomicFileForUserId(userId: Int): AtomicFile {
+ return atomicFile
+ }
+ })
}
@After
@@ -174,10 +205,12 @@
}
private fun setupKeyGestureController() {
- keyGestureController = KeyGestureController(context, testLooper.looper)
+ keyGestureController =
+ KeyGestureController(context, testLooper.looper, inputDataStore)
Mockito.`when`(iInputManager.getAppLaunchBookmarks())
.thenReturn(keyGestureController.appLaunchBookmarks)
keyGestureController.systemRunning()
+ testLooper.dispatchAll()
}
private fun notifyHomeGestureCompleted() {
@@ -542,9 +575,9 @@
),
TestData(
"META + C -> Launch Default Contacts",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_C),
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_P),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_C),
+ intArrayOf(KeyEvent.KEYCODE_P),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
@@ -560,9 +593,9 @@
),
TestData(
"META + K -> Launch Default Calendar",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_K),
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_C),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_K),
+ intArrayOf(KeyEvent.KEYCODE_C),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALENDAR)
@@ -577,24 +610,6 @@
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MAPS)
),
TestData(
- "META + P -> Launch Default Music",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_P),
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_P),
- KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MUSIC)
- ),
- TestData(
- "META + S -> Launch Default SMS",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_S),
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_S),
- KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_SMS)
- ),
- TestData(
"META + U -> Launch Default Calculator",
intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_U),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
@@ -839,10 +854,10 @@
AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER)
),
TestData(
- "META + C -> Launch Default Contacts",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_C),
+ "META + P -> Launch Default Contacts",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_P),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_C),
+ intArrayOf(KeyEvent.KEYCODE_P),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
@@ -857,10 +872,10 @@
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_EMAIL)
),
TestData(
- "META + K -> Launch Default Calendar",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_K),
+ "META + C -> Launch Default Calendar",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_C),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_K),
+ intArrayOf(KeyEvent.KEYCODE_C),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALENDAR)
@@ -875,24 +890,6 @@
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MAPS)
),
TestData(
- "META + P -> Launch Default Music",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_P),
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_P),
- KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MUSIC)
- ),
- TestData(
- "META + S -> Launch Default SMS",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_S),
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_S),
- KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
- AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_SMS)
- ),
- TestData(
"META + U -> Launch Default Calculator",
intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_U),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
@@ -915,14 +912,14 @@
AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER)
),
TestData(
- "META + SHIFT + C -> Launch Default Contacts",
+ "META + SHIFT + P -> Launch Default Contacts",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_SHIFT_LEFT,
- KeyEvent.KEYCODE_C
+ KeyEvent.KEYCODE_P
),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_C),
+ intArrayOf(KeyEvent.KEYCODE_P),
KeyEvent.META_META_ON or KeyEvent.META_SHIFT_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
@@ -1424,6 +1421,45 @@
testKeyGestureInternal(test)
}
+ @Test
+ @Parameters(method = "customInputGesturesTestArguments")
+ fun testCustomKeyGesturesSavedAndLoadedByController(test: TestData) {
+ val userId = 10
+ setupKeyGestureController()
+ val builder = InputGestureData.Builder()
+ .setKeyGestureType(test.expectedKeyGestureType)
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ test.expectedKeys[0],
+ test.expectedModifierState
+ )
+ )
+ if (test.expectedAppLaunchData != null) {
+ builder.setAppLaunchData(test.expectedAppLaunchData)
+ }
+ val inputGestureData = builder.build()
+
+ keyGestureController.setCurrentUserId(userId)
+ testLooper.dispatchAll()
+ keyGestureController.addCustomInputGesture(userId, inputGestureData.aidlData)
+ testLooper.dispatchAll()
+
+ // Reinitialize the gesture controller simulating a login/logout for the user.
+ setupKeyGestureController()
+ keyGestureController.setCurrentUserId(userId)
+ testLooper.dispatchAll()
+ val savedInputGestures = keyGestureController.getCustomInputGestures(userId, null)
+ assertEquals(
+ "Test: $test doesn't produce correct number of saved input gestures",
+ 1,
+ savedInputGestures.size
+ )
+ assertEquals(
+ "Test: $test doesn't produce correct input gesture data", inputGestureData,
+ InputGestureData(savedInputGestures[0])
+ )
+ }
+
class TouchpadTestData(
val name: String,
val touchpadGestureType: Int,
@@ -1502,6 +1538,39 @@
keyGestureController.unregisterKeyGestureHandler(handler, 0)
}
+ @Test
+ @Parameters(method = "customTouchpadGesturesTestArguments")
+ fun testCustomTouchpadGesturesSavedAndLoadedByController(test: TouchpadTestData) {
+ val userId = 10
+ setupKeyGestureController()
+ val builder = InputGestureData.Builder()
+ .setKeyGestureType(test.expectedKeyGestureType)
+ .setTrigger(InputGestureData.createTouchpadTrigger(test.touchpadGestureType))
+ if (test.expectedAppLaunchData != null) {
+ builder.setAppLaunchData(test.expectedAppLaunchData)
+ }
+ val inputGestureData = builder.build()
+ keyGestureController.setCurrentUserId(userId)
+ testLooper.dispatchAll()
+ keyGestureController.addCustomInputGesture(userId, inputGestureData.aidlData)
+ testLooper.dispatchAll()
+
+ // Reinitialize the gesture controller simulating a login/logout for the user.
+ setupKeyGestureController()
+ keyGestureController.setCurrentUserId(userId)
+ testLooper.dispatchAll()
+ val savedInputGestures = keyGestureController.getCustomInputGestures(userId, null)
+ assertEquals(
+ "Test: $test doesn't produce correct number of saved input gestures",
+ 1,
+ savedInputGestures.size
+ )
+ assertEquals(
+ "Test: $test doesn't produce correct input gesture data", inputGestureData,
+ InputGestureData(savedInputGestures[0])
+ )
+ }
+
private fun testKeyGestureInternal(test: TestData) {
val handledEvents = mutableListOf<KeyGestureEvent>()
val handler = KeyGestureHandler { event, _ ->
@@ -1627,4 +1696,4 @@
return true
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/NetworkSecurityConfigTest/res/xml/ct_domains.xml b/tests/NetworkSecurityConfigTest/res/xml/ct_domains.xml
new file mode 100644
index 0000000..67d4397a
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/ct_domains.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <base-config>
+ <certificateTransparency enabled="true" />
+ </base-config>
+ <domain-config>
+ <domain>android.com</domain>
+ <trust-anchors>
+ <certificates src="system" />
+ </trust-anchors>
+ </domain-config>
+ <domain-config>
+ <domain>subdomain_user.android.com</domain>
+ <trust-anchors>
+ <certificates src="user" />
+ </trust-anchors>
+ </domain-config>
+ <domain-config>
+ <certificateTransparency enabled="true" />
+ <domain>subdomain_user_ct.android.com</domain>
+ <trust-anchors>
+ <certificates src="user" />
+ </trust-anchors>
+ </domain-config>
+ <domain-config>
+ <domain>subdomain_inline.android.com</domain>
+ <trust-anchors>
+ <certificates src="@raw/ca_certs_pem" />
+ </trust-anchors>
+ </domain-config>
+ <domain-config>
+ <certificateTransparency enabled="true" />
+ <domain>subdomain_inline_ct.android.com</domain>
+ <trust-anchors>
+ <certificates src="@raw/ca_certs_pem" />
+ </trust-anchors>
+ </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/ct_users.xml b/tests/NetworkSecurityConfigTest/res/xml/ct_users.xml
new file mode 100644
index 0000000..c35fd71
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/ct_users.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <base-config>
+ <trust-anchors>
+ <certificates src="user" />
+ </trust-anchors>
+ </base-config>
+ <domain-config>
+ <domain>android.com</domain>
+ </domain-config>
+ <domain-config>
+ <certificateTransparency enabled="true" />
+ <domain>subdomain.android.com</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java
index 0494f17..c6fe068 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java
@@ -111,7 +111,8 @@
private NetworkSecurityConfig getSystemStoreConfig() {
return new NetworkSecurityConfig.Builder()
.addCertificatesEntryRef(
- new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+ new CertificatesEntryRef(
+ SystemCertificateSource.getInstance(), false, false))
.build();
}
@@ -141,7 +142,8 @@
NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
.setPinSet(new PinSet(pins, Long.MAX_VALUE))
.addCertificatesEntryRef(
- new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+ new CertificatesEntryRef(
+ SystemCertificateSource.getInstance(), false, false))
.build();
ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
= new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
@@ -159,7 +161,8 @@
NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
.setPinSet(new PinSet(pins, Long.MAX_VALUE))
.addCertificatesEntryRef(
- new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+ new CertificatesEntryRef(
+ SystemCertificateSource.getInstance(), false, false))
.build();
ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
= new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
@@ -178,7 +181,8 @@
NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
.setPinSet(new PinSet(pins, Long.MAX_VALUE))
.addCertificatesEntryRef(
- new CertificatesEntryRef(SystemCertificateSource.getInstance(), true))
+ new CertificatesEntryRef(
+ SystemCertificateSource.getInstance(), true, false))
.build();
ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
= new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
@@ -245,7 +249,8 @@
NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
.setPinSet(new PinSet(pins, Long.MAX_VALUE))
.addCertificatesEntryRef(
- new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+ new CertificatesEntryRef(
+ SystemCertificateSource.getInstance(), false, false))
.build();
ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
= new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
index 81e05c1..542465d 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
@@ -502,4 +502,47 @@
TestUtils.assertConnectionSucceeds(context, "android.com", 443);
TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
}
+
+ public void testCertificateTransparencyDomainConfig() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.ct_domains,
+ TestUtils.makeApplicationInfo());
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ assertTrue(appConfig.hasPerDomainConfigs());
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("");
+ assertNotNull(config);
+ // Check defaults.
+ assertTrue(config.isCertificateTransparencyVerificationRequired());
+
+ config = appConfig.getConfigForHostname("android.com");
+ assertTrue(config.isCertificateTransparencyVerificationRequired());
+
+ config = appConfig.getConfigForHostname("subdomain_user.android.com");
+ assertFalse(config.isCertificateTransparencyVerificationRequired());
+
+ config = appConfig.getConfigForHostname("subdomain_user_ct.android.com");
+ assertTrue(config.isCertificateTransparencyVerificationRequired());
+
+ config = appConfig.getConfigForHostname("subdomain_inline.android.com");
+ assertFalse(config.isCertificateTransparencyVerificationRequired());
+
+ config = appConfig.getConfigForHostname("subdomain_inline_ct.android.com");
+ assertTrue(config.isCertificateTransparencyVerificationRequired());
+ }
+
+ public void testCertificateTransparencyUserConfig() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.ct_users,
+ TestUtils.makeApplicationInfo());
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ assertTrue(appConfig.hasPerDomainConfigs());
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("");
+ assertNotNull(config);
+ // Check defaults.
+ assertFalse(config.isCertificateTransparencyVerificationRequired());
+
+ config = appConfig.getConfigForHostname("android.com");
+ assertFalse(config.isCertificateTransparencyVerificationRequired());
+
+ config = appConfig.getConfigForHostname("subdomain.android.com");
+ assertTrue(config.isCertificateTransparencyVerificationRequired());
+ }
}
diff --git a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
index af87bf7..49616c3 100644
--- a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
@@ -83,6 +83,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
@@ -112,6 +113,7 @@
private final TestClock mTestClock = new TestClock();
private TestLooper mTestLooper;
+ private Executor mTestExecutor;
private Context mSpyContext;
// Keep track of all created watchdogs to apply device config changes
private List<PackageWatchdog> mAllocatedWatchdogs;
@@ -141,6 +143,7 @@
Manifest.permission.WRITE_DEVICE_CONFIG,
Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG);
mTestLooper = new TestLooper();
+ mTestExecutor = mTestLooper.getNewExecutor();
mSpyContext = spy(InstrumentationRegistry.getContext());
when(mSpyContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.getPackageInfo(anyString(), anyInt())).then(inv -> {
@@ -231,31 +234,37 @@
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(1);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(2);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(3);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(3);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(4);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(4);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(5);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(5);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(6);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(6);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(7);
}
@@ -272,6 +281,7 @@
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
verify(rollbackObserver).onExecuteBootLoopMitigation(1);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
@@ -281,6 +291,7 @@
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rollbackObserver).onExecuteBootLoopMitigation(2);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(3);
@@ -289,6 +300,7 @@
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(3);
}
@@ -305,18 +317,21 @@
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(1);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(1);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(2);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(3);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(3);
verify(rollbackObserver).onExecuteBootLoopMitigation(1);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
@@ -326,24 +341,28 @@
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(3);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(4);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(4);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(5);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(5);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(6);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(6);
verify(rollbackObserver).onExecuteBootLoopMitigation(2);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(3);
@@ -352,6 +371,7 @@
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(6);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(7);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(3);
@@ -362,6 +382,7 @@
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(1);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
}
@@ -379,12 +400,14 @@
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(1);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(1);
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
verify(rollbackObserver).onExecuteBootLoopMitigation(1);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
@@ -394,6 +417,7 @@
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
verify(rollbackObserver).onExecuteBootLoopMitigation(2);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(3);
@@ -402,6 +426,7 @@
watchdog.noteBoot();
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(2);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(3);
verify(rollbackObserver, never()).onExecuteBootLoopMitigation(3);
@@ -412,6 +437,7 @@
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
verify(rescuePartyObserver).onExecuteBootLoopMitigation(1);
verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
}
@@ -739,14 +765,14 @@
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(e);
}
- watchdog.registerHealthObserver(rollbackObserver);
+ watchdog.registerHealthObserver(rollbackObserver, mTestExecutor);
return rollbackObserver;
}
RescuePartyObserver setUpRescuePartyObserver(PackageWatchdog watchdog) {
setCrashRecoveryPropRescueBootCount(0);
RescuePartyObserver rescuePartyObserver = spy(RescuePartyObserver.getInstance(mSpyContext));
assertFalse(RescueParty.isRebootPropertySet());
- watchdog.registerHealthObserver(rescuePartyObserver);
+ watchdog.registerHealthObserver(rescuePartyObserver, mTestExecutor);
return rescuePartyObserver;
}
diff --git a/tests/PackageWatchdog/src/com/android/server/ExplicitHealthCheckServiceTest.java b/tests/PackageWatchdog/src/com/android/server/ExplicitHealthCheckServiceTest.java
index cd2ab86..055e159 100644
--- a/tests/PackageWatchdog/src/com/android/server/ExplicitHealthCheckServiceTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/ExplicitHealthCheckServiceTest.java
@@ -16,6 +16,8 @@
package com.android.server;
+import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_HEALTH_CHECK_PASSED_PACKAGE;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
@@ -35,8 +37,6 @@
private ExplicitHealthCheckService mExplicitHealthCheckService;
private static final String PACKAGE_NAME = "com.test.package";
- private static final String EXTRA_HEALTH_CHECK_PASSED_PACKAGE =
- "android.service.watchdog.extra.health_check_passed_package";
@Before
public void setup() throws Exception {
@@ -52,7 +52,7 @@
IBinder binder = mExplicitHealthCheckService.onBind(new Intent());
CountDownLatch countDownLatch = new CountDownLatch(1);
RemoteCallback callback = new RemoteCallback(result -> {
- assertThat(result.get(EXTRA_HEALTH_CHECK_PASSED_PACKAGE))
+ assertThat(result.getString(EXTRA_HEALTH_CHECK_PASSED_PACKAGE))
.isEqualTo(PACKAGE_NAME);
countDownLatch.countDown();
});
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 5a8a6be..c64dc72 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -53,6 +53,7 @@
import android.provider.DeviceConfig;
import android.util.AtomicFile;
import android.util.LongArrayQueue;
+import android.util.Slog;
import android.util.Xml;
import androidx.test.InstrumentationRegistry;
@@ -88,6 +89,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -119,6 +121,7 @@
private final TestClock mTestClock = new TestClock();
private TestLooper mTestLooper;
+ private Executor mTestExecutor;
private Context mSpyContext;
// Keep track of all created watchdogs to apply device config changes
private List<PackageWatchdog> mAllocatedWatchdogs;
@@ -155,6 +158,7 @@
Manifest.permission.WRITE_DEVICE_CONFIG,
Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG);
mTestLooper = new TestLooper();
+ mTestExecutor = mTestLooper.getNewExecutor();
mSpyContext = spy(InstrumentationRegistry.getContext());
when(mSpyContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.getPackageInfo(anyString(), anyInt())).then(inv -> {
@@ -226,7 +230,8 @@
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
@@ -242,8 +247,10 @@
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer2, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
new VersionedPackage(APP_B, VERSION_CODE)),
@@ -260,7 +267,8 @@
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
watchdog.unregisterHealthObserver(observer);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
@@ -276,8 +284,10 @@
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer2, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A), SHORT_DURATION);
watchdog.unregisterHealthObserver(observer2);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
@@ -294,7 +304,8 @@
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
moveTimeForwardAndDispatch(SHORT_DURATION);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
@@ -310,8 +321,10 @@
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer2, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A), LONG_DURATION);
moveTimeForwardAndDispatch(SHORT_DURATION);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
@@ -330,13 +343,14 @@
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
// Start observing APP_A
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
// Then advance time half-way
moveTimeForwardAndDispatch(SHORT_DURATION / 2);
// Start observing APP_A again
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
// Then advance time such that it should have expired were it not for the second observation
moveTimeForwardAndDispatch((SHORT_DURATION / 2) + 1);
@@ -358,15 +372,17 @@
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog1.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog1.startObservingHealth(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
+ watchdog1.registerHealthObserver(observer1, mTestExecutor);
+ watchdog1.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog1.registerHealthObserver(observer2, mTestExecutor);
+ watchdog1.startExplicitHealthCheck(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
// Then advance time and run IO Handler so file is saved
mTestLooper.dispatchAll();
// Then start a new watchdog
PackageWatchdog watchdog2 = createWatchdog();
// Then resume observer1 and observer2
- watchdog2.registerHealthObserver(observer1);
- watchdog2.registerHealthObserver(observer2);
+ watchdog2.registerHealthObserver(observer1, mTestExecutor);
+ watchdog2.registerHealthObserver(observer2, mTestExecutor);
raiseFatalFailureAndDispatch(watchdog2,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
new VersionedPackage(APP_B, VERSION_CODE)),
@@ -374,6 +390,7 @@
// We should receive failed packages as expected to ensure observers are persisted and
// resumed correctly
+ mTestLooper.dispatchAll();
assertThat(observer1.mHealthCheckFailedPackages).containsExactly(APP_A);
assertThat(observer2.mHealthCheckFailedPackages).containsExactly(APP_A, APP_B);
}
@@ -387,8 +404,10 @@
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer2, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
// Then fail APP_A below the threshold
for (int i = 0; i < watchdog.getTriggerFailureCount() - 1; i++) {
@@ -414,9 +433,10 @@
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
-
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_B), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer2, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_B), SHORT_DURATION);
// Then fail APP_C (not observed) above the threshold
raiseFatalFailureAndDispatch(watchdog,
@@ -448,7 +468,8 @@
}
};
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
// Then fail APP_A (different version) above the threshold
raiseFatalFailureAndDispatch(watchdog,
@@ -477,13 +498,17 @@
PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
// Start observing for all impact observers
- watchdog.startObservingHealth(observerNone, Arrays.asList(APP_A, APP_B, APP_C, APP_D),
+ watchdog.registerHealthObserver(observerNone, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerNone, Arrays.asList(APP_A, APP_B, APP_C, APP_D),
SHORT_DURATION);
- watchdog.startObservingHealth(observerHigh, Arrays.asList(APP_A, APP_B, APP_C),
+ watchdog.registerHealthObserver(observerHigh, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerHigh, Arrays.asList(APP_A, APP_B, APP_C),
SHORT_DURATION);
- watchdog.startObservingHealth(observerMid, Arrays.asList(APP_A, APP_B),
+ watchdog.registerHealthObserver(observerMid, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerMid, Arrays.asList(APP_A, APP_B),
SHORT_DURATION);
- watchdog.startObservingHealth(observerLow, Arrays.asList(APP_A),
+ watchdog.registerHealthObserver(observerLow, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerLow, Arrays.asList(APP_A),
SHORT_DURATION);
// Then fail all apps above the threshold
@@ -523,13 +548,17 @@
PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
// Start observing for all impact observers
- watchdog.startObservingHealth(observerNone, Arrays.asList(APP_A, APP_B, APP_C, APP_D),
+ watchdog.registerHealthObserver(observerNone, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerNone, Arrays.asList(APP_A, APP_B, APP_C, APP_D),
SHORT_DURATION);
- watchdog.startObservingHealth(observerHigh, Arrays.asList(APP_A, APP_B, APP_C),
+ watchdog.registerHealthObserver(observerHigh, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerHigh, Arrays.asList(APP_A, APP_B, APP_C),
SHORT_DURATION);
- watchdog.startObservingHealth(observerMid, Arrays.asList(APP_A, APP_B),
+ watchdog.registerHealthObserver(observerMid, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerMid, Arrays.asList(APP_A, APP_B),
SHORT_DURATION);
- watchdog.startObservingHealth(observerLow, Arrays.asList(APP_A),
+ watchdog.registerHealthObserver(observerLow, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerLow, Arrays.asList(APP_A),
SHORT_DURATION);
// Then fail all apps above the threshold
@@ -577,8 +606,10 @@
PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
// Start observing for observerFirst and observerSecond with failure handling
- watchdog.startObservingHealth(observerFirst, Arrays.asList(APP_A), LONG_DURATION);
- watchdog.startObservingHealth(observerSecond, Arrays.asList(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(observerFirst, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerFirst, Arrays.asList(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(observerSecond, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerSecond, Arrays.asList(APP_A), LONG_DURATION);
// Then fail APP_A above the threshold
raiseFatalFailureAndDispatch(watchdog,
@@ -641,8 +672,10 @@
PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
// Start observing for observerFirst and observerSecond with failure handling
- watchdog.startObservingHealth(observerFirst, Arrays.asList(APP_A), LONG_DURATION);
- watchdog.startObservingHealth(observerSecond, Arrays.asList(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(observerFirst, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerFirst, Arrays.asList(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(observerSecond, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observerSecond, Arrays.asList(APP_A), LONG_DURATION);
// Then fail APP_A above the threshold
raiseFatalFailureAndDispatch(watchdog,
@@ -709,8 +742,10 @@
PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
// Start observing for observer1 and observer2 with failure handling
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer2, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
// Then fail APP_A above the threshold
raiseFatalFailureAndDispatch(watchdog,
@@ -731,8 +766,10 @@
PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
// Start observing for observer1 and observer2 with failure handling
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer2, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
// Then fail APP_A above the threshold
raiseFatalFailureAndDispatch(watchdog,
@@ -762,8 +799,10 @@
// Start observing with explicit health checks for APP_A and APP_B respectively
// with observer1 and observer2
controller.setSupportedPackages(Arrays.asList(APP_A, APP_B));
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_B), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer2, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_B), SHORT_DURATION);
// Run handler so requests are dispatched to the controller
mTestLooper.dispatchAll();
@@ -779,7 +818,8 @@
// Observer3 didn't exist when we got the explicit health check above, so
// it starts out with a non-passing explicit health check and has to wait for a pass
// otherwise it would be notified of APP_A failure on expiry
- watchdog.startObservingHealth(observer3, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer3, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer3, Arrays.asList(APP_A), SHORT_DURATION);
// Then expire observers
moveTimeForwardAndDispatch(SHORT_DURATION);
@@ -809,8 +849,9 @@
// Start observing with explicit health checks for APP_A and APP_B
controller.setSupportedPackages(Arrays.asList(APP_A, APP_B, APP_C));
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer, Arrays.asList(APP_B), LONG_DURATION);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_B), LONG_DURATION);
// Run handler so requests are dispatched to the controller
mTestLooper.dispatchAll();
@@ -846,7 +887,7 @@
// Then set new supported packages
controller.setSupportedPackages(Arrays.asList(APP_C));
// Start observing APP_A and APP_C; only APP_C has support for explicit health checks
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A, APP_C), SHORT_DURATION);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A, APP_C), SHORT_DURATION);
// Run handler so requests/cancellations are dispatched to the controller
mTestLooper.dispatchAll();
@@ -877,7 +918,8 @@
// package observation duration == LONG_DURATION
// health check duration == SHORT_DURATION (set by default in the TestController)
controller.setSupportedPackages(Arrays.asList(APP_A));
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), LONG_DURATION);
// Then APP_A has exceeded health check duration
moveTimeForwardAndDispatch(SHORT_DURATION);
@@ -908,7 +950,8 @@
// package observation duration == SHORT_DURATION / 2
// health check duration == SHORT_DURATION (set by default in the TestController)
controller.setSupportedPackages(Arrays.asList(APP_A));
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION / 2);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION / 2);
// Forward time to expire the observation duration
moveTimeForwardAndDispatch(SHORT_DURATION / 2);
@@ -981,7 +1024,7 @@
// Start observing with failure handling
TestObserver observer = new TestObserver(OBSERVER_NAME_1,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
- wd.startObservingHealth(observer, Collections.singletonList(APP_A), SHORT_DURATION);
+ wd.startExplicitHealthCheck(observer, Collections.singletonList(APP_A), SHORT_DURATION);
// Notify of NetworkStack failure
mConnectivityModuleCallbackCaptor.getValue().onNetworkStackFailure(APP_A);
@@ -1001,7 +1044,7 @@
// Start observing with failure handling
TestObserver observer = new TestObserver(OBSERVER_NAME_1,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
- wd.startObservingHealth(observer, Collections.singletonList(APP_A), SHORT_DURATION);
+ wd.startExplicitHealthCheck(observer, Collections.singletonList(APP_A), SHORT_DURATION);
// Notify of NetworkStack failure
mConnectivityModuleCallbackCaptor.getValue().onNetworkStackFailure(APP_A);
@@ -1022,7 +1065,8 @@
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), SHORT_DURATION);
// Fail APP_A below the threshold which should not trigger package failures
for (int i = 0; i < PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT - 1; i++) {
watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
@@ -1050,7 +1094,8 @@
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A, APP_B), Long.MAX_VALUE);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A, APP_B), Long.MAX_VALUE);
watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS + 1);
@@ -1075,15 +1120,16 @@
}
/**
- * Test default monitoring duration is used when PackageWatchdog#startObservingHealth is offered
- * an invalid durationMs.
+ * Test default monitoring duration is used when PackageWatchdog#startExplicitHealthCheck is
+ * offered an invalid durationMs.
*/
@Test
public void testInvalidMonitoringDuration_beforeExpiry() {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), -1);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), -1);
// Note: Don't move too close to the expiration time otherwise the handler will be thrashed
// by PackageWatchdog#scheduleNextSyncStateLocked which keeps posting runnables with very
// small timeouts.
@@ -1097,15 +1143,16 @@
}
/**
- * Test default monitoring duration is used when PackageWatchdog#startObservingHealth is offered
- * an invalid durationMs.
+ * Test default monitoring duration is used when PackageWatchdog#startExplicitHealthCheck is
+ * offered an invalid durationMs.
*/
@Test
public void testInvalidMonitoringDuration_afterExpiry() {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), -1);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), -1);
moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS + 1);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
@@ -1127,7 +1174,8 @@
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), Long.MAX_VALUE);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, Arrays.asList(APP_A), Long.MAX_VALUE);
// Raise 2 failures at t=0 and t=900 respectively
watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
@@ -1154,8 +1202,10 @@
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_B), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer2, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer2, Arrays.asList(APP_B), SHORT_DURATION);
raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_CRASH);
@@ -1174,7 +1224,8 @@
PackageWatchdog watchdog = createWatchdog();
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, Arrays.asList(APP_A), SHORT_DURATION);
raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
VERSION_CODE)), PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
@@ -1194,7 +1245,8 @@
persistentObserver.setPersistent(true);
persistentObserver.setMayObservePackages(true);
- watchdog.startObservingHealth(persistentObserver, Arrays.asList(APP_B), SHORT_DURATION);
+ watchdog.registerHealthObserver(persistentObserver, mTestExecutor);
+ watchdog.startExplicitHealthCheck(persistentObserver, Arrays.asList(APP_B), SHORT_DURATION);
raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN);
@@ -1212,7 +1264,8 @@
persistentObserver.setPersistent(true);
persistentObserver.setMayObservePackages(false);
- watchdog.startObservingHealth(persistentObserver, Arrays.asList(APP_B), SHORT_DURATION);
+ watchdog.registerHealthObserver(persistentObserver, mTestExecutor);
+ watchdog.startExplicitHealthCheck(persistentObserver, Arrays.asList(APP_B), SHORT_DURATION);
raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN);
@@ -1223,13 +1276,15 @@
/** Ensure that boot loop mitigation is done when the number of boots meets the threshold. */
@Test
public void testBootLoopDetection_meetsThreshold() {
+ Slog.w("hrm1243", "I should definitely be here try 1 ");
mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(bootObserver);
+ watchdog.registerHealthObserver(bootObserver, mTestExecutor);
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
assertThat(bootObserver.mitigatedBootLoop()).isTrue();
}
@@ -1237,10 +1292,11 @@
public void testBootLoopDetection_meetsThresholdRecoverability() {
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(bootObserver);
+ watchdog.registerHealthObserver(bootObserver, mTestExecutor);
for (int i = 0; i < 15; i++) {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
assertThat(bootObserver.mitigatedBootLoop()).isTrue();
}
@@ -1252,10 +1308,11 @@
public void testBootLoopDetection_doesNotMeetThreshold() {
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(bootObserver);
+ watchdog.registerHealthObserver(bootObserver, mTestExecutor);
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT - 1; i++) {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
assertThat(bootObserver.mitigatedBootLoop()).isFalse();
}
@@ -1268,10 +1325,11 @@
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
- watchdog.registerHealthObserver(bootObserver);
+ watchdog.registerHealthObserver(bootObserver, mTestExecutor);
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT - 1; i++) {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
assertThat(bootObserver.mitigatedBootLoop()).isFalse();
}
@@ -1286,11 +1344,12 @@
bootObserver1.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
TestObserver bootObserver2 = new TestObserver(OBSERVER_NAME_2);
bootObserver2.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
- watchdog.registerHealthObserver(bootObserver1);
- watchdog.registerHealthObserver(bootObserver2);
+ watchdog.registerHealthObserver(bootObserver1, mTestExecutor);
+ watchdog.registerHealthObserver(bootObserver2, mTestExecutor);
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
assertThat(bootObserver1.mitigatedBootLoop()).isTrue();
assertThat(bootObserver2.mitigatedBootLoop()).isFalse();
}
@@ -1302,11 +1361,12 @@
bootObserver1.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
TestObserver bootObserver2 = new TestObserver(OBSERVER_NAME_2);
bootObserver2.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
- watchdog.registerHealthObserver(bootObserver1);
- watchdog.registerHealthObserver(bootObserver2);
+ watchdog.registerHealthObserver(bootObserver1, mTestExecutor);
+ watchdog.registerHealthObserver(bootObserver2, mTestExecutor);
for (int i = 0; i < 15; i++) {
watchdog.noteBoot();
}
+ mTestLooper.dispatchAll();
assertThat(bootObserver1.mitigatedBootLoop()).isTrue();
assertThat(bootObserver2.mitigatedBootLoop()).isFalse();
}
@@ -1319,7 +1379,7 @@
mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(bootObserver);
+ watchdog.registerHealthObserver(bootObserver, mTestExecutor);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; j++) {
watchdog.noteBoot();
@@ -1333,7 +1393,7 @@
watchdog.noteBoot();
}
}
-
+ mTestLooper.dispatchAll();
assertThat(bootObserver.mBootMitigationCounts).isEqualTo(List.of(1, 2, 3, 4, 1, 2, 3, 4));
}
@@ -1342,7 +1402,7 @@
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
- watchdog.registerHealthObserver(bootObserver);
+ watchdog.registerHealthObserver(bootObserver, mTestExecutor);
for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT - 1; j++) {
watchdog.noteBoot();
}
@@ -1358,7 +1418,7 @@
for (int i = 0; i < 4; i++) {
watchdog.noteBoot();
}
-
+ mTestLooper.dispatchAll();
assertThat(bootObserver.mBootMitigationCounts).isEqualTo(List.of(1, 2, 3, 4, 1, 2, 3, 4));
}
@@ -1370,7 +1430,8 @@
public void testNullFailedPackagesList() {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer1, List.of(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(observer1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer1, List.of(APP_A), LONG_DURATION);
raiseFatalFailureAndDispatch(watchdog, null, PackageWatchdog.FAILURE_REASON_APP_CRASH);
assertThat(observer1.mMitigatedPackages).isEmpty();
@@ -1388,18 +1449,18 @@
PackageWatchdog watchdog = createWatchdog(testController, true);
TestObserver testObserver1 = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(testObserver1);
- watchdog.startObservingHealth(testObserver1, List.of(APP_A), LONG_DURATION);
+ watchdog.registerHealthObserver(testObserver1, mTestExecutor);
+ watchdog.startExplicitHealthCheck(testObserver1, List.of(APP_A), LONG_DURATION);
mTestLooper.dispatchAll();
TestObserver testObserver2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.registerHealthObserver(testObserver2);
- watchdog.startObservingHealth(testObserver2, List.of(APP_B), LONG_DURATION);
+ watchdog.registerHealthObserver(testObserver2, mTestExecutor);
+ watchdog.startExplicitHealthCheck(testObserver2, List.of(APP_B), LONG_DURATION);
mTestLooper.dispatchAll();
TestObserver testObserver3 = new TestObserver(OBSERVER_NAME_3);
- watchdog.registerHealthObserver(testObserver3);
- watchdog.startObservingHealth(testObserver3, List.of(APP_C), LONG_DURATION);
+ watchdog.registerHealthObserver(testObserver3, mTestExecutor);
+ watchdog.startExplicitHealthCheck(testObserver3, List.of(APP_C), LONG_DURATION);
mTestLooper.dispatchAll();
watchdog.unregisterHealthObserver(testObserver1);
@@ -1431,14 +1492,15 @@
public void testFailureHistoryIsPreserved() {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer, List.of(APP_A), SHORT_DURATION);
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, List.of(APP_A), SHORT_DURATION);
for (int i = 0; i < PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT - 1; i++) {
watchdog.notifyPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
}
mTestLooper.dispatchAll();
assertThat(observer.mMitigatedPackages).isEmpty();
- watchdog.startObservingHealth(observer, List.of(APP_A), LONG_DURATION);
+ watchdog.startExplicitHealthCheck(observer, List.of(APP_A), LONG_DURATION);
watchdog.notifyPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
mTestLooper.dispatchAll();
@@ -1453,7 +1515,8 @@
public void testMitigationSlidingWindow() {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
- watchdog.startObservingHealth(observer, List.of(APP_A),
+ watchdog.registerHealthObserver(observer, mTestExecutor);
+ watchdog.startExplicitHealthCheck(observer, List.of(APP_A),
PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS * 2);
@@ -1895,6 +1958,7 @@
}
public boolean onExecuteBootLoopMitigation(int level) {
+ Slog.w("hrm1243", "I'm here " + level);
mMitigatedBootLoop = true;
mBootMitigationCounts.add(level);
return true;
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 76be232..74db6a5 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -659,7 +659,6 @@
private void verifySetSafeModeAlarm(
boolean safeModeEnabledByCaller,
- boolean safeModeConfigFlagEnabled,
boolean expectingSafeModeEnabled)
throws Exception {
final VcnGatewayConnectionConfig config =
@@ -670,7 +669,6 @@
mock(VcnGatewayConnection.Dependencies.class);
setUpWakeupMessage(
mSafeModeTimeoutAlarm, VcnGatewayConnection.SAFEMODE_TIMEOUT_ALARM, deps);
- doReturn(safeModeConfigFlagEnabled).when(mFeatureFlags).safeModeConfig();
final VcnGatewayConnection connection =
new VcnGatewayConnection(
@@ -694,37 +692,19 @@
}
@Test
- public void testSafeModeEnabled_configFlagEnabled() throws Exception {
+ public void testSafeModeEnabled() throws Exception {
verifySetSafeModeAlarm(
true /* safeModeEnabledByCaller */,
- true /* safeModeConfigFlagEnabled */,
true /* expectingSafeModeEnabled */);
}
@Test
- public void testSafeModeEnabled_configFlagDisabled() throws Exception {
- verifySetSafeModeAlarm(
- true /* safeModeEnabledByCaller */,
- false /* safeModeConfigFlagEnabled */,
- true /* expectingSafeModeEnabled */);
- }
-
- @Test
- public void testSafeModeDisabled_configFlagEnabled() throws Exception {
+ public void testSafeModeDisabled() throws Exception {
verifySetSafeModeAlarm(
false /* safeModeEnabledByCaller */,
- true /* safeModeConfigFlagEnabled */,
false /* expectingSafeModeEnabled */);
}
- @Test
- public void testSafeModeDisabled_configFlagDisabled() throws Exception {
- verifySetSafeModeAlarm(
- false /* safeModeEnabledByCaller */,
- false /* safeModeConfigFlagEnabled */,
- true /* expectingSafeModeEnabled */);
- }
-
private Consumer<VcnNetworkAgent> setupNetworkAndGetUnwantedCallback() {
triggerChildOpened();
mTestLooper.dispatchAll();
diff --git a/tools/processors/property_cache/src/java/android/processor/property_cache/CachedPropertyProcessor.java b/tools/processors/property_cache/src/java/android/processor/property_cache/CachedPropertyProcessor.java
index 0361012..c438163 100644
--- a/tools/processors/property_cache/src/java/android/processor/property_cache/CachedPropertyProcessor.java
+++ b/tools/processors/property_cache/src/java/android/processor/property_cache/CachedPropertyProcessor.java
@@ -30,6 +30,7 @@
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
@@ -42,6 +43,11 @@
new IpcDataCacheComposer();
@Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
public Set<String> getSupportedAnnotationTypes() {
return new HashSet<String>(
ImmutableSet.of(CachedPropertyDefaults.class.getCanonicalName()));
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt
index 100d869..4a6d4b1 100644
--- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt
+++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt
@@ -17,8 +17,10 @@
package com.android.systemfeatures
import android.annotation.SdkConstant
+import com.squareup.javapoet.ClassName
import com.squareup.javapoet.FieldSpec
import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.TypeSpec
import java.io.IOException
import javax.annotation.processing.AbstractProcessor
@@ -27,6 +29,7 @@
import javax.lang.model.SourceVersion
import javax.lang.model.element.Modifier
import javax.lang.model.element.TypeElement
+import javax.lang.model.element.VariableElement
import javax.tools.Diagnostic
/*
@@ -35,7 +38,16 @@
* <p>The output is a single class file, `com.android.internal.pm.SystemFeaturesMetadata`, with
* properties computed from feature constant definitions in the PackageManager class. This
* class is only produced if the processed environment includes PackageManager; all other
- * invocations are ignored.
+ * invocations are ignored. The generated API is as follows:
+ *
+ * <pre>
+ * package android.content.pm;
+ * public final class SystemFeaturesMetadata {
+ * public static final int SDK_FEATURE_COUNT;
+ * // @return [0, SDK_FEATURE_COUNT) if an SDK-defined system feature, -1 otherwise.
+ * public static int maybeGetSdkFeatureIndex(String featureName);
+ * }
+ * </pre>
*/
class SystemFeaturesMetadataProcessor : AbstractProcessor() {
@@ -56,19 +68,31 @@
return false
}
- // We're only interested in feature constants defined in PackageManager.
- var featureCount = 0
- roundEnv.getElementsAnnotatedWith(SdkConstant::class.java).forEach {
- if (
- it.enclosingElement == packageManagerType &&
- it.getAnnotation(SdkConstant::class.java).value ==
- SdkConstant.SdkConstantType.FEATURE
- ) {
- featureCount++
- }
- }
+ // Collect all FEATURE-annotated fields from PackageManager, and
+ // 1) Use the field values to de-duplicate, as there can be multiple FEATURE_* fields that
+ // map to the same feature string name value.
+ // 2) Ensure they're sorted to ensure consistency and determinism between builds.
+ val featureVarNames =
+ roundEnv
+ .getElementsAnnotatedWith(SdkConstant::class.java)
+ .asSequence()
+ .filter {
+ it.enclosingElement == packageManagerType &&
+ it.getAnnotation(SdkConstant::class.java).value ==
+ SdkConstant.SdkConstantType.FEATURE
+ }
+ .mapNotNull { element ->
+ (element as? VariableElement)?.let { varElement ->
+ varElement.getConstantValue()?.toString() to
+ varElement.simpleName.toString()
+ }
+ }
+ .toMap()
+ .values
+ .sorted()
+ .toList()
- if (featureCount == 0) {
+ if (featureVarNames.isEmpty()) {
// This is fine, and happens for any environment that doesn't include PackageManager.
return false
}
@@ -77,16 +101,8 @@
TypeSpec.classBuilder("SystemFeaturesMetadata")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addJavadoc("@hide")
- .addField(
- FieldSpec.builder(Int::class.java, "SDK_FEATURE_COUNT")
- .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
- .addJavadoc(
- "The number of `@SdkConstant` features defined in PackageManager."
- )
- .addJavadoc("@hide")
- .initializer("\$L", featureCount)
- .build()
- )
+ .addField(buildFeatureCount(featureVarNames))
+ .addMethod(buildFeatureIndexLookup(featureVarNames))
.build()
try {
@@ -104,7 +120,41 @@
return true
}
+ private fun buildFeatureCount(featureVarNames: Collection<String>): FieldSpec {
+ return FieldSpec.builder(Int::class.java, "SDK_FEATURE_COUNT")
+ .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
+ .addJavadoc(
+ "# of {@link android.annotation.SdkConstant}` features defined in PackageManager."
+ )
+ .addJavadoc("\n\n@hide")
+ .initializer("\$L", featureVarNames.size)
+ .build()
+ }
+
+ private fun buildFeatureIndexLookup(featureVarNames: Collection<String>): MethodSpec {
+ val methodBuilder =
+ MethodSpec.methodBuilder("maybeGetSdkFeatureIndex")
+ .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+ .addJavadoc("@return an index in [0, SDK_FEATURE_COUNT) for features defined ")
+ .addJavadoc("in PackageManager, else -1.")
+ .addJavadoc("\n\n@hide")
+ .returns(Int::class.java)
+ .addParameter(String::class.java, "featureName")
+ methodBuilder.beginControlFlow("switch (featureName)")
+ featureVarNames.forEachIndexed { index, featureVarName ->
+ methodBuilder
+ .addCode("case \$T.\$N: ", PACKAGEMANAGER_CLASS, featureVarName)
+ .addStatement("return \$L", index)
+ }
+ methodBuilder
+ .addCode("default: ")
+ .addStatement("return -1")
+ .endControlFlow()
+ return methodBuilder.build()
+ }
+
companion object {
private val SDK_CONSTANT_ANNOTATION_NAME = SdkConstant::class.qualifiedName
+ private val PACKAGEMANAGER_CLASS = ClassName.get("android.content.pm", "PackageManager")
}
}
diff --git a/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java b/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java
index 4ffb5b9..74ce6da 100644
--- a/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java
+++ b/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java
@@ -16,10 +16,16 @@
package com.android.systemfeatures;
+import static com.android.internal.pm.SystemFeaturesMetadata.maybeGetSdkFeatureIndex;
+
import static com.google.common.truth.Truth.assertThat;
+import android.content.pm.PackageManager;
+
import com.android.internal.pm.SystemFeaturesMetadata;
+import com.google.common.collect.Range;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -33,4 +39,17 @@
// It defines 5 annotated features, and any/all other constants should be ignored.
assertThat(SystemFeaturesMetadata.SDK_FEATURE_COUNT).isEqualTo(5);
}
+
+ @Test
+ public void testSdkFeatureIndex() {
+ // Only SDK-defined features return valid indices.
+ final Range validIndexRange = Range.closedOpen(0, SystemFeaturesMetadata.SDK_FEATURE_COUNT);
+ assertThat(maybeGetSdkFeatureIndex(PackageManager.FEATURE_PC)).isIn(validIndexRange);
+ assertThat(maybeGetSdkFeatureIndex(PackageManager.FEATURE_VULKAN)).isIn(validIndexRange);
+ assertThat(maybeGetSdkFeatureIndex(PackageManager.FEATURE_NOT_ANNOTATED)).isEqualTo(-1);
+ assertThat(maybeGetSdkFeatureIndex(PackageManager.NOT_FEATURE)).isEqualTo(-1);
+ assertThat(maybeGetSdkFeatureIndex("foo")).isEqualTo(-1);
+ assertThat(maybeGetSdkFeatureIndex("0")).isEqualTo(-1);
+ assertThat(maybeGetSdkFeatureIndex("")).isEqualTo(-1);
+ }
}